此版本仍在开发中,目前尚不被视为稳定版本。如需最新稳定版本,请使用 spring-cloud-contract 5.0.2spring-doc.cadn.net.cn

使用存根运行程序启动应用

警告

由于当前构件仓库发布工具的限制,我们目前无法发布可执行JAR文件,且自4.1.6版本起,我们将跳过该构件的发布。Stub Runner Boot仍可通过Docker Stub Runner Boot镜像获得,这是使用该应用的首选方式。您也可以访问项目仓库中的源码并自行构建该应用。若构件仓库工具链完成所需调整,我们将恢复发布此JAR文件。spring-doc.cadn.net.cn

Spring Cloud Contract Stub Runner Boot 是一个 Spring Boot 应用程序,它暴露 REST 端点以触发消息标签并访问 WireMock 服务器。spring-doc.cadn.net.cn

Stub Runner Boot Security

Stub Runner Boot 应用程序默认未进行安全保护——即使某些存根实际上并不需要安全保护,为所有存根添加安全措施仍需额外工作。由于这是一个测试工具,服务器不打算在生产环境中使用。spring-doc.cadn.net.cn

预期只有受信任的客户端才能访问 Stub Runner Boot 服务器。你不应该在不可信的位置以 Fat Jar 或 Docker 镜像 的形式运行此应用程序。

存根运行器服务器

要使用存根运行器服务器,请添加以下依赖项:spring-doc.cadn.net.cn

compile "org.springframework.cloud:spring-cloud-starter-stub-runner"

然后使用 @EnableStubRunnerServer 注解一个类,构建一个可执行的胖JAR包,即可开始工作。spring-doc.cadn.net.cn

有关属性信息,请参见 Stub Runner Spring 部分。spring-doc.cadn.net.cn

存根运行器服务器胖JAR

您可以从 Maven 下载独立的 JAR 文件(例如,对于版本 2.0.1.RELEASE)<br/>通过运行以下命令:spring-doc.cadn.net.cn

$ wget -O stub-runner.jar 'https://search.maven.org/remotecontent?filepath=org/springframework/cloud/spring-cloud-contract-stub-runner-boot/2.0.1.RELEASE/spring-cloud-contract-stub-runner-boot-2.0.1.RELEASE.jar'
$ java -jar stub-runner.jar --spring.cloud.contract.stubrunner.ids=... --spring.cloud.contract.stubrunner.repositoryRoot=...

Spring Cloud CLI

从 Spring Cloud CLI 项目 0 版本开始,您可以通过运行 spring cloud stubrunner 来启动 Stub Runner Boot。spring-doc.cadn.net.cn

要传递配置,您可以在当前工作目录中创建一个 spring.cloud.contract.stubrunner.yml 文件,或在名为 config 的子目录中创建,或在 ~/.spring-cloud 中创建。该文件可类似于以下示例,用于运行本地安装的存根:spring-doc.cadn.net.cn

示例 1. stubrunner.yml
spring.cloud.contract.stubrunner:
  stubsMode: LOCAL
  ids:
    - com.example:beer-api-producer:+:9876

然后您可以在终端窗口中调用 spring cloud stubrunner 来启动 Stub Runner 服务器。它在端口 8750 上可用。spring-doc.cadn.net.cn

端点

Stub Runner Boot 提供两个端点:spring-doc.cadn.net.cn

HTTP

对于 HTTP,Stub Runner Boot 提供以下端点:spring-doc.cadn.net.cn

  • GET /stubs: 返回以 ivy:integer 格式表示的所有正在运行的存根(stubs)列表spring-doc.cadn.net.cn

  • GET /stubs/{ivy}: 返回指定 ivy 符号对应的端口(当调用端点 ivy 时,artifactId 仅可为 artifactIdspring-doc.cadn.net.cn

消息传递

对于消息传递,Stub Runner Boot 提供以下端点:spring-doc.cadn.net.cn

  • GET /triggers: 返回以 ivy : [ label1, label2 …​] 标记法表示的所有正在运行的标签列表spring-doc.cadn.net.cn

  • POST /triggers/{label}: 运行一个带有 label 的触发器spring-doc.cadn.net.cn

  • POST /triggers/{ivy}/{label}: 运行一个触发器,使用给定 labelivy 符号进行触发
    (在调用该端点时,ivy 也可以是 artifactId 仅限于此)spring-doc.cadn.net.cn

例举

以下示例展示了 Stub Runner Boot 的典型用法:spring-doc.cadn.net.cn

@SpringBootTest(classes = StubRunnerBoot, properties = "spring.cloud.zookeeper.enabled=false")
@ActiveProfiles("test")
class StubRunnerBootSpec {

	@Autowired
	StubRunning stubRunning

	@BeforeEach
	void setup() {
		RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning),
				new TriggerController(stubRunning))
	}

	@Test
	void 'should return a list of running stub servers in "full ivy port" notation'() {
		when:
			String response = RestAssuredMockMvc.get('/stubs').body.asString()
		then:
			def root = new JsonSlurper().parseText(response)
			assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs' instanceof Integer
	}

	@Test
	void 'should return a port on which a #stubId stub is running'() {
		given:
		def stubIds = ['org.springframework.cloud.contract.verifier.stubs:bootService:+:stubs',
				   'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs',
				   'org.springframework.cloud.contract.verifier.stubs:bootService:+',
				   'org.springframework.cloud.contract.verifier.stubs:bootService',
				   'bootService']
		stubIds.each {
			when:
				def response = RestAssuredMockMvc.get("/stubs/${it}")
			then:
				assert response.statusCode == 200
				assert Integer.valueOf(response.body.asString()) > 0
		}
	}

	@Test
	void 'should return 404 when missing stub was called'() {
		when:
			def response = RestAssuredMockMvc.get("/stubs/a:b:c:d")
		then:
			assert response.statusCode == 404
	}

	@Test
	void 'should return a list of messaging labels that can be triggered when version and classifier are passed'() {
		when:
			String response = RestAssuredMockMvc.get('/triggers').body.asString()
		then:
			def root = new JsonSlurper().parseText(response)
			assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["return_book_1"])
	}

	@Test
	void 'should trigger a messaging label'() {
		given:
			StubRunning stubRunning = Mockito.mock(StubRunning)
			RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
		when:
			def response = RestAssuredMockMvc.post("/triggers/delete_book")
		then:
			response.statusCode == 200
		and:
			Mockito.verify(stubRunning).trigger('delete_book')
	}

	@Test
	void 'should trigger a messaging label for a stub with #stubId ivy notation'() {
		given:
			StubRunning stubRunning = Mockito.mock(StubRunning)
			RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
		and:
			def stubIds = ['org.springframework.cloud.contract.verifier.stubs:bootService:stubs', 'org.springframework.cloud.contract.verifier.stubs:bootService', 'bootService']
		stubIds.each {
			when:
				def response = RestAssuredMockMvc.post("/triggers/$it/delete_book")
			then:
				assert response.statusCode == 200
			and:
				Mockito.verify(stubRunning).trigger(it, 'delete_book')
		}

	}

	@Test
	void 'should throw exception when trigger is missing'() {
		when:
		BDDAssertions.thenThrownBy(() -> RestAssuredMockMvc.post("/triggers/missing_label"))
		.hasMessageContaining("Exception occurred while trying to return [missing_label] label.")
		.hasMessageContaining("Available labels are")
		.hasMessageContaining("org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]")
		.hasMessageContaining("org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs=")
	}

}

Stub Runner Boot with Service Discovery

使用 Stub Runner Boot 的一种方式是将其用作“冒烟测试”的桩服务源。这是什么意思呢?</p><p>假设您不希望将 50 个微服务部署到测试环境中,以查看您的应用程序是否正常工作。您已经在构建过程中运行了一套测试,但还希望确保您的应用程序打包正确。您可以将应用程序部署到一个环境中,启动它,并运行几项测试,以检查其是否能正常工作。我们可以将这些测试称为“冒烟测试”,因为它们的目的仅是为了验证少量的测试场景。spring-doc.cadn.net.cn

这种方法的问题在于,如果您使用微服务,很可能也会使用服务发现工具。Stub Runner Boot 可通过启动所需的存根并将其注册到服务发现工具中来解决此问题。spring-doc.cadn.net.cn

现在假设我们希望启动该应用程序,以便自动注册存根(stubs)。我们可以通过运行应用程序并传入 java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar 来实现,其中 ${SYSTEM_PROPS}spring-doc.cadn.net.cn

这样,您已部署的应用程序就可以通过服务发现机制向已启动的 WireMock 服务器发送请求。很可能,第 1 到第 3 点在 application.yml 中默认即可设置,因为它们不太可能发生变化。这样,您只需在每次启动 Stub Runner Boot 时提供要下载的存根列表即可。spring-doc.cadn.net.cn