跳到主要内容

使用 Stub Runner Boot 应用程序

DeepSeek V3 中英对照 Using the Stub Runner Boot Application

警告

由于当前制品仓库发布工具的限制,我们目前无法发布可执行的jar文件,从4.1.6版本开始,我们将跳过此制品的发布。Stub Runner Boot仍然可以通过Docker Stub Runner Boot镜像获取,这是使用该应用程序的首选方式。您也可以访问项目仓库中的源代码并自行构建应用程序。如果在制品仓库工具中进行了必要的调整,我们将恢复发布此jar文件。

Spring Cloud Contract Stub Runner Boot 是一个 Spring Boot 应用程序,它暴露了 REST 端点来触发消息标签并访问 WireMock 服务器。

Stub Runner Boot 安全​

Stub Runner Boot 应用程序在设计上并不安全——保护它需要为所有存根添加安全性,即使它们实际上并不需要。由于这是一个测试工具,该服务器不打算在生产环境中使用。

important

预期情况下,只有受信任的客户端才能访问 Stub Runner Boot 服务器。您不应在不受信任的位置将此应用程序作为 Fat Jar 或 Docker 镜像 运行。

Stub Runner Server​

要使用 Stub Runner Server,请添加以下依赖:

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

然后使用 @EnableStubRunnerServer 注解标注一个类,构建一个 fat jar,即可准备就绪。

有关属性,请参阅 Stub Runner Spring 部分。

Stub Runner Server Fat Jar​

你可以从 Maven 下载一个独立的 JAR(例如,针对版本 2.0.1.RELEASE),通过运行以下命令:

$ 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 项目的 1.4.0.RELEASE 版本开始,你可以通过运行 spring cloud stubrunner 来启动 Stub Runner Boot。

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

示例 1. stubrunner.yml

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

然后,你可以在终端窗口中调用 spring cloud stubrunner 来启动 Stub Runner 服务器。它将在端口 8750 上可用。

端点​

Stub Runner Boot 提供两个端点:

HTTP​

对于 HTTP,Stub Runner Boot 提供了以下端点:

  • GET /stubs:返回所有正在运行的存根列表,采用 ivy:integer 表示法

  • GET /stubs/{ivy}:返回给定 ivy 表示法的端口(调用端点时,ivy 也可以是仅包含 artifactId 的形式)

消息传递​

对于消息传递,Stub Runner Boot 提供以下端点:

  • GET /triggers: 返回所有正在运行的标签列表,格式为 ivy : [ label1, label2 …​]

  • POST /triggers/{label}: 运行指定 label 的触发器

  • POST /triggers/{ivy}/{label}: 为给定的 ivy 表示法运行指定 label 的触发器(调用端点时,ivy 也可以是仅包含 artifactId 的形式)

示例​

以下示例展示了 Stub Runner Boot 的典型用法:

@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 与服务发现​

使用 Stub Runner Boot 的一种方式是将其作为“冒烟测试”的存根数据源。这是什么意思呢?假设你不想为了验证应用程序是否正常工作而将 50 个微服务部署到测试环境中。你已经在构建过程中运行了一系列测试,但你还想确保应用程序的打包工作正常。你可以将应用程序部署到某个环境,启动它,并运行一些测试来检查它是否正常工作。我们可以将这些测试称为“冒烟测试”,因为它们的目的只是检查少数几个测试场景。

这种方法的问题在于,如果你使用微服务,很可能也会使用服务发现工具。Stub Runner Boot 允许你通过启动所需的存根并在服务发现工具中注册它们来解决这个问题。

现在假设我们希望启动这个应用程序,以便自动注册存根。我们可以通过运行 java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar 来实现,其中 ${SYSTEM_PROPS}。

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