Spring Cloud 合约功能(四)

时间:2022-12-01 11:00:50

Spring Cloud 合约功能(四)

5.5. 存根流道弹簧云

Stub Runner可以与Spring Cloud集成。

有关实际生活示例,请参阅:

  • 生产者应用程序示例
  • 使用者应用程序示例

5.5.1. 存根服务发现

最重要的特点是它存根:​​Stub Runner Spring Cloud​

  • ​DiscoveryClient​
  • ​ReactorServiceInstanceLoadBalancer​

这意味着,无论您使用动物园管理员,领事,尤里卡还是任何东西。 否则,您在测试中不需要它。我们正在启动您的 WireMock 实例 依赖项,我们告诉您的应用程序,无论何时使用,都要加载 平衡直接调用那些存根服务器 而不是调用真正的服务发现工具。​​Feign​​​​RestTemplate​​​​DiscoveryClient​

例如,以下测试通过:

def 'should make service discovery work'() {
expect: 'WireMocks are running'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Stubs can be reached via load service discovery'
restTemplate.getForObject('http://loanIssuance/name', String) == 'loanIssuance'
restTemplate.getForObject('http://someNameThatShouldMapFraudDetectionServer/name', String) == 'fraudDetectionServer'
}

请注意,前面的示例需要以下配置文件:

stubrunner:
idsToServiceIds:
ivyNotation: someValueInsideYourCode
fraudDetectionServer: someNameThatShouldMapFraudDetectionServer
测试配置文件和服务发现

在集成测试中,您通常不希望调用发现服务(如 Eureka) 或配置服务器。这就是您创建要在其中禁用的其他测试配置的原因 这些功能。

由于春云公地的某些限制, 为此,您必须禁用这些属性 在静态块中,例如以下示例(对于 Eureka):

//Hack to work around https://github.com/spring-cloud/spring-cloud-commons/issues/156
static {
System.setProperty("eureka.client.enabled", "false");
System.setProperty("spring.cloud.config.failFast", "false");
}

5.5.2. 附加配置

您可以使用 themap 将存根与应用程序的名称进行匹配。​​artifactId​​​​stubrunner.idsToServiceIds:​

默认情况下,所有服务发现都是存根。这意味着,无论您是否有 一个现有的,其结果将被忽略。但是,如果你想重用它,你可以设置,然后你现有的结果是 与存根的合并。​​DiscoveryClient​​​​stubrunner.cloud.delegate.enabled​​​​true​​​​DiscoveryClient​

存根运行器使用的默认 Maven 配置可以调整 通过设置以下系统属性或设置相应的环境变量:

  • ​maven.repo.local​​:自定义 maven 本地存储库位置的路径
  • ​org.apache.maven.user-settings​​:自定义 maven 用户设置位置的路径
  • ​org.apache.maven.global-settings​​:指向 maven 全局设置位置的路径

5.6. 使用存根运行器引导应用程序

Spring Cloud 合约存根运行程序启动是一个 Spring 启动应用程序,它将 REST 端点公开给 触发消息标签并访问 WireMock 服务器。

5.6.1. 存根运行程序引导安全性

存根运行程序启动应用程序在设计上不受保护 - 保护它需要为所有应用程序添加安全性 存根,即使它们实际上并不需要它。由于这是一个测试实用程序 - 服务器不适合在生产环境中使用。

预计只有受信任的客户端才能访问存根运行程序引导服务器。你不应该 在不受信任的位置将此应用程序作为胖罐或Docker 映像运行。

5.6.2. 存根运行服务器

要使用存根运行器服务器,请添加以下依赖项:

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

然后用注释一个类,构建一个胖罐子,它就可以工作了。​​@EnableStubRunnerServer​

有关属性,请参阅存根流道弹簧部分。

5.6.3. 存根运行器服务器胖罐

您可以从 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 --stubrunner.ids=... --stubrunner.repositoryRoot=...

5.6.4. 春云命令行界面

从Spring Cloud CLI项目的版本开始,您可以通过运行来启动存根运行器引导。​​1.4.0.RELEASE​​​​spring cloud stubrunner​

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

例 2.stubrunner.yml

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

然后,您可以从终端窗口调用以启动 存根运行器服务器。它在港口可用。​​spring cloud stubrunner​​​​8750​

5.6.5. 端点

存根运行程序启动提供两个终结点:

  • HTTP
  • 消息
HTTP

对于 HTTP,存根运行程序启动使以下终结点可用:

  • GET:返回所有正在运行的存根注释的列表/stubsivy:integer
  • GET:返回给定表示法的端口(调用端点时也可以是)/stubs/{ivy}ivyivyartifactId
消息

对于消息传递,存根运行程序引导使以下端点可用:

  • GET:返回所有正在运行的标签注释的列表/triggersivy : [ label1, label2 …​]
  • 开机自检:运行触发器/triggers/{label}label
  • POST:运行带有给定符号的触发器 (调用端点时,也可以只)/triggers/{ivy}/{label}labelivyivyartifactId

5.6.6. 例子

以下示例显示了存根运行程序引导的典型用法:

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

@Autowired
StubRunning stubRunning

def setup() {
RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning),
new TriggerController(stubRunning))
}

def '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)
root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs' instanceof Integer
}

def 'should return a port on which a [#stubId] stub is running'() {
when:
def response = RestAssuredMockMvc.get("/stubs/${stubId}")
then:
response.statusCode == 200
Integer.valueOf(response.body.asString()) > 0
where:
stubId << ['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']
}

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

def '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)
root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["delete_book", "return_book_1", "return_book_2"])
}

def 'should trigger a messaging label'() {
given:
StubRunning stubRunning = Mock()
RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
when:
def response = RestAssuredMockMvc.post("/triggers/delete_book")
then:
response.statusCode == 200
and:
1 * stubRunning.trigger('delete_book')
}

def 'should trigger a messaging label for a stub with [#stubId] ivy notation'() {
given:
StubRunning stubRunning = Mock()
RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
when:
def response = RestAssuredMockMvc.post("/triggers/$stubId/delete_book")
then:
response.statusCode == 200
and:
1 * stubRunning.trigger(stubId, 'delete_book')
where:
stubId << ['org.springframework.cloud.contract.verifier.stubs:bootService:stubs', 'org.springframework.cloud.contract.verifier.stubs:bootService', 'bootService']
}

def 'should throw exception when trigger is missing'() {
when:
RestAssuredMockMvc.post("/triggers/missing_label")
then:
Exception e = thrown(Exception)
e.message.contains("Exception occurred while trying to return [missing_label] label.")
e.message.contains("Available labels are")
e.message.contains("org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]")
e.message.contains("org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs=")
}

}

5.6.7. 使用服务发现的存根运行程序引导

使用存根运行器引导的一种方法是将其用作“冒烟测试”的存根馈送。那是什么意思? 假设您不想按顺序将 50 个微服务部署到测试环境 以查看您的应用程序是否正常工作。您已经在构建过程中运行了一套测试, 但您还希望确保应用程序的打包正常工作。您可以 将应用程序部署到环境中,启动它,然后对其运行几个测试以查看是否 它有效。我们可以称这些测试为“烟雾测试”,因为它们的目的是只检查少数几个 的测试场景。

这种方法的问题在于,如果你使用微服务,你很可能也 使用服务发现工具。存根运行程序引导允许您通过启动 所需的存根,并在服务发现工具中注册它们。考虑以下示例 尤里卡的这种设置(假设尤里卡已经在运行):

@SpringBootApplication
@EnableStubRunnerServer
@EnableEurekaClient
@AutoConfigureStubRunner
public class StubRunnerBootEurekaExample {

public static void main(String[] args) {
SpringApplication.run(StubRunnerBootEurekaExample.class, args);
}

}

我们要启动存根运行器引导服务器(),启用尤里卡客户端(), 并打开存根运行程序功能()。​​@EnableStubRunnerServer​​​​@EnableEurekaClient​​​​@AutoConfigureStubRunner​

现在假设我们要启动此应用程序,以便自动注册存根。 我们可以通过运行应用程序来做到这一点,其中包含以下属性列表:​​java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar​​​​${SYSTEM_PROPS}​

* -Dstubrunner.repositoryRoot=https://repo.spring.io/snapshot (1)
* -Dstubrunner.cloud.stubbed.discovery.enabled=false (2)
* -Dstubrunner.ids=org.springframework.cloud.contract.verifier.stubs:loanIssuance,org.
* springframework.cloud.contract.verifier.stubs:fraudDetectionServer,org.springframework.
* cloud.contract.verifier.stubs:bootService (3)
* -Dstubrunner.idsToServiceIds.fraudDetectionServer=
* someNameThatShouldMapFraudDetectionServer (4)
*
* (1) - we tell Stub Runner where all the stubs reside (2) - we don't want the default
* behaviour where the discovery service is stubbed. That's why the stub registration will
* be picked (3) - we provide a list of stubs to download (4) - we provide a list of

这样,您部署的应用程序可以通过服务向启动的 WireMock 服务器发送请求 发现。最有可能的是,点 1 到 3 可以默认设置,因为它们不是 可能会改变。这样,您只需提供启动时要下载的存根列表 存根运行器引导。​​application.yml​

5.7. 消费者驱动的合同:每个消费者的存根

在某些情况下,同一终结点的两个使用者希望有两个不同的响应。

此方法还可以让您立即知道哪个使用者使用 API 的哪个部分。 您可以删除 API 生成的部分响应,并查看哪些自动生成的测试 失败。如果没有失败,您可以安全地删除响应的该部分,因为没有人使用它。

考虑以下为称为 它有两个使用者(和):​​producer​​​​foo-consumer​​​​bar-consumer​

消费者​​foo-service​

request {
url '/foo'
method GET()
}
response {
status OK()
body(
foo: "foo"
}
}

消费者​​bar-service​

request {
url '/bar'
method GET()
}
response {
status OK()
body(
bar: "bar"
}
}

不能为同一请求生成两个不同的响应。这就是为什么您可以正确打包 合同,然后从该功能中获利。​​stubsPerConsumer​

在生产者端,使用者可以拥有一个包含仅与他们相关的合同的文件夹。 通过将 theflag 设置为 ,我们不再注册所有存根,而只注册那些 对应于使用者应用程序的名称。换句话说,我们扫描每个存根的路径,并且, 如果它包含路径中具有使用者名称的子文件夹,则只有这样才会注册。​​stubrunner.stubs-per-consumer​​​​true​

在生产者方面,合同看起来像这样​​foo​

.
└── contracts
├── bar-consumer
│ ├── bookReturnedForBar.groovy
│ └── shouldCallBar.groovy
└── foo-consumer
├── bookReturnedForFoo.groovy
└── shouldCallFoo.groovy

消费者可以设置理论或 theto,或者,您可以按如下方式设置测试:​​bar-consumer​​​​spring.application.name​​​​stubrunner.consumer-name​​​​bar-consumer​

@SpringBootTest(classes = Config, properties = ["spring.application.name=bar-consumer"])
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerSpec extends Specification {
...
}

然后,仅允许引用在其名称中包含路径(即文件夹中的路径)下注册的存根。​​bar-consumer​​​​src/test/resources/contracts/bar-consumer/some/contracts/…​

您还可以显式设置使用者名称,如下所示:

@SpringBootTest(classes = Config)
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
consumerName = "foo-consumer",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
...
}

然后,只允许引用在包含其名称的路径下注册的存根(即文件夹中的存根)。​​foo-consumer​​​​src/test/resources/contracts/foo-consumer/some/contracts/…​

5.8. 从一个位置获取存根或合约定义

而不是从中选取存根或协定定义 Artifactory,Nexus或Git,你可以指向 驱动器或类路径上的位置。这样做在多模块项目中特别有用,其中一个模块需要 重用另一个模块的存根或协定,而无需 需要在本地 Maven 中实际安装它们 存储库以将这些更改提交到 Git。

为了实现这一点,您可以在设置存储库根参数时使用协议 在存根运行器或 Spring Cloud 合约插件中。​​stubs://​

在此示例中,项目已成功 在文件夹下生成了构建和存根。作为使用者,可以使用协议设置存根运行程序以从该位置选取存根。​​producer​​​​target/stubs​​​​stubs://​

@AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "stubs://file://location/to/the/producer/target/stubs/",
ids = "com.example:some-producer")

合同和存根可以存储在一个位置,每个生产者都有自己的专用文件夹,用于合同和存根映射。在该文件夹下,每个使用者都可以有自己的设置。若要使存根运行程序从提供的 ID 中找到专用文件夹,可以传递属性或系统属性。 以下清单显示了合同和存根的排列:​​stubs.find-producer=true​​​​stubrunner.stubs.find-producer=true​

└── com.example 
├── some-artifact-id
│ └── 0.0.1
│ ├── contracts
│ │ └── shouldReturnStuffForArtifactId.groovy
│ └── mappings
│ └── shouldReturnStuffForArtifactId.json
└── some-other-artifact-id
├── contracts
│ └── shouldReturnStuffForOtherArtifactId.groovy
└── mappings
└── shouldReturnStuffForOtherArtifactId.json

使用者的组 ID

具有工件 ID 的使用者 [某些工件 ID]

具有工件 ID [某些工件 ID] 的使用者的合约

具有项目 ID 的使用者的映射 [some-artifact-id]

具有工件 ID 的使用者 [其他工件 ID]

@AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "stubs://file://location/to/the/contracts/directory",
ids = "com.example:some-producer",
properties="stubs.find-producer=true")

5.9. 在运行时生成存根

作为使用者,您可能不希望等待生成者完成其实现,然后发布其存根。此问题的解决方案可以在运行时生成存根。

作为生产者,定义合约时,您需要使生成的测试通过,以便发布存根。在某些情况下,您希望取消阻止使用者,以便他们可以在您的测试实际通过之前获取存根。在这种情况下,应将此类合同设置为进行中。您可以在“正在进行的合同”部分下阅读有关此内容的更多信息。这样,不会生成测试,但会生成存根。

作为使用者,您可以切换开关以在运行时生成存根。存根运行程序忽略所有现有的存根映射,并为所有协定定义生成新的映射。另一种选择是传递系统属性。以下示例显示了此类设置:​​stubrunner.generate-stubs​

@AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "stubs://file://location/to/the/contracts",
ids = "com.example:some-producer",
generateStubs = true)

5.10. 无存根失败

默认情况下,如果未找到存根,存根运行程序将失败。若要更改该行为,请在注释中设置属性或在 JUnit 规则或扩展上调用该方法。以下示例演示如何执行此操作:​​failOnNoStubs​​​​false​​​​withFailOnNoStubs(false)​

@AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "stubs://file://location/to/the/contracts",
ids = "com.example:some-producer",
failOnNoStubs = false)

5.11. 通用属性

本节简要介绍常见属性,包括:

  • JUnit 和 Spring 的通用属性
  • 存根运行程序存根 ID

5.11.1. JUnit 和 Spring 的通用属性

您可以使用系统属性或 Spring 配置来设置重复属性 性能。下表显示了其名称及其默认值:

属性名称

默认值

描述

​stubrunner.minPort​

​10000​

带有存根的已启动 WireMock 的端口的最小值。

​stubrunner.maxPort​

​15000​

带有存根的已启动 WireMock 的端口的最大值。

​stubrunner.repositoryRoot​

Maven repository URL.如果为空,则调用本地 Maven 存储库。

​stubrunner.classifier​

​stubs​

存根项目的默认分类器。

​stubrunner.stubsMode​

​CLASSPATH​

要获取和注册存根的方式。

​stubrunner.ids​

要下载的常春藤符号存根数组。

​stubrunner.username​

可选用户名,用于访问存储 JAR 的工具 存根。

​stubrunner.password​

可选密码,用于访问存储 JAR 的工具 存根。

​stubrunner.stubsPerConsumer​

​false​

设置为 如果要将不同的存根用于 每个使用者,而不是为每个使用者注册所有存根。​​true​

​stubrunner.consumerName​

如果要为每个使用者使用存根,并且想要 覆盖使用者名称,更改此值。

5.11.2. 存根运行器存根 ID

您可以在系统属性中设置要下载的存根。他们 使用以下模式:​​stubrunner.ids​

groupId:artifactId:version:classifier:port

请注意,和是可选的。​​version​​​​classifier​​​​port​

  • 如果您不提供,则随机选择一个。port
  • 如果未提供,则使用默认值。(请注意,您可以 以这种方式传递一个空分类器:)。classifiergroupId:artifactId:version:
  • 如果您不提供,则通过,最新的是 下载。version+

​port​​表示 WireMock 服务器的端口。

从版本 1.0.4 开始,您可以提供一系列版本 希望存根运行器考虑在内。您可以阅读有关 以太版本控制范围在这里。

6. 春云合约电汇模拟

Spring Cloud 合约 WireMock 模块允许您在 弹簧启动应用程序。有关更多详细信息,请查看示例。

如果你有一个使用 Tomcat 作为嵌入式服务器的 Spring Boot 应用程序(这是 默认为),您可以添加到类路径中,并在测试中使用 Wiremock。Wiremock 作为存根服务器运行,而您 可以通过使用 Java API 或使用静态 JSON 声明作为 您的测试。以下代码显示了一个示例:​​spring-boot-starter-web​​​​spring-cloud-starter-contract-stub-runner​​​​@AutoConfigureWireMock​

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock(port = 0)
public class WiremockForDocsTests {

// A service that calls out over HTTP
@Autowired
private Service service;

@BeforeEach
public void setup() {
this.service.setBase("http://localhost:"
+ this.environment.getProperty("wiremock.server.port"));
}

// Using the WireMock APIs in the normal way:
@Test
public void contextLoads() throws Exception {
// Stubbing WireMock
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World!");
}

}

要在其他端口上启动存根服务器,请使用(例如)。对于随机端口,请使用值 of。存根 服务器端口可以在测试应用程序上下文中与属性绑定。使用添加类型到 测试应用程序上下文,在方法和类之间缓存 具有相同的上下文。Spring 集成测试也是如此。另外,您可以 将类型的 bean 注入到您的测试中。 注册的 WireMock 服务器在每个测试类后重置。 但是,如果需要在每个测试方法之后重置它,请将属性设置为 。​​@AutoConfigureWireMock(port=9999)​​​​0​​​​wiremock.server.port​​​​@AutoConfigureWireMock​​​​WiremockConfiguration​​​​WireMockServer​​​​wiremock.reset-mappings-after-each-test​​​​true​

6.1. 自动注册存根

如果使用,它会从文件中注册 WireMock JSON 存根 系统或类路径(缺省情况下为 from)。您可以 通过使用注释中的属性自定义位置,该属性可以是 Ant 样式的资源模式或目录。在目录的情况下,是 附加。以下代码显示了一个示例:​​@AutoConfigureWireMock​​​​file:src/test/resources/mappings​​​​stubs​​​*/.json​

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {

@Autowired
private Service service;

@Test
public void contextLoads() throws Exception {
assertThat(this.service.go()).isEqualTo("Hello World!");
}

}

实际上,WireMock总是从as加载映射。 以及属性中的自定义位置。若要更改此行为,可以 还要指定文件根目录,如本文档的下一节​所述。​​src/test/resources/mappings​​​​stubs​

此外,位置中的映射不被视为Wiremock的“默认映射”和调用的一部分 在测试期间不会导致映射 在包含的位置。但是,在每个测试类之后重置映射(包括从存根位置添加映射),并且可以选择 在每种测试方法之后(由财产保护)。​​stubs​​​​com.github.tomakehurst.wiremock.client.WireMock.resetToDefaultMappings​​​​stubs​​​​org.springframework.cloud.contract.wiremock.WireMockTestExecutionListener​​​​wiremock.reset-mappings-after-each-test​

如果您使用春云合约的默认存根罐,您的 存根存储在文件夹中。 如果要从该位置注册所有嵌入式 JAR 中的所有存根,可以使用 以下语法:​​/META-INF/group-id/artifact-id/versions/mappings/​

@AutoConfigureWireMock(port = 0, stubs = "classpath*:/META-INF/**/mappings/**/*.json")

6.2. 使用文件指定存根主体

WireMock 可以从类路径或文件系统上的文件中读取响应正文。在 在文件系统的情况下,您可以在 JSON DSL 中看到响应具有 a而不是 (字面意思)。这些文件是相对于根目录解析的(默认情况下)。要自定义此位置,可以将注释中的属性设置为父项的位置 目录(换句话说,是一个子目录)。您可以使用 Spring 资源 表示法以引用位置。通用网址不是 支持。可以给出一个值列表 — 在这种情况下,WireMock 解析第一个文件 当它需要找到响应正文时,它就存在。​​bodyFileName​​​​body​​​​src/test/resources/__files​​​​files​​​​@AutoConfigureWireMock​​​​__files​​​​file:…​​​​classpath:…​

配置根时,它还会影响 自动加载存根,因为它们来自根位置 在名为的子目录中。​​files​​​​mappings​

的值没有 对从属性显式加载的存根的影响。​​files​​​​stubs​

6.3. 替代方案:使用 JUnit 规则

对于更传统的 WireMock 体验,您可以使用 JUnitto 启动和停止 服务器。为此,请使用便利类获取实例,如以下示例所示:​​@Rules​​​​WireMockSpring​​​​Options​

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class WiremockForDocsClassRuleTests {

// Start WireMock on some dynamic port
// for some reason `dynamicPort()` is not working properly
public static WireMockServer wiremock = new WireMockServer(WireMockSpring.options().dynamicPort());

@BeforeAll
static void setupClass() {
wiremock.start();
}

@AfterEach
void after() {
wiremock.resetAll();
}

@AfterAll
static void clean() {
wiremock.shutdown();
}

// A service that calls out over HTTP to wiremock's port
@Autowired
private Service service;

@BeforeEach
public void setup() {
this.service.setBase("http://localhost:" + wiremock.port());
}

// Using the WireMock APIs in the normal way:
@Test
public void contextLoads() throws Exception {
// Stubbing WireMock
wiremock.stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World!");
}

}

这意味着服务器在此类中的所有方法之后关闭 已经运行。​​@ClassRule​

6.4. 放宽 SSL 验证的 rest 模板

WireMock允许您使用URL协议存根“安全”服务器。如果你的 应用程序想要在集成测试中联系该存根服务器,它发现 SSL 证书无效(自安装证书的常见问题)。 最好的选择通常是重新配置要使用的客户端。如果这不是 选项,您可以要求 Spring 配置忽略 SSL 验证错误的 HTTP 客户端 (当然,这样做仅用于测试)。​​https​​​​http​

为了以最小的麻烦完成这项工作,您需要使用Spring Bootin您的应用程序,如以下示例所示:​​RestTemplateBuilder​

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}

你需要因为构建器是通过回调传递到 初始化它,以便此时可以在客户端中设置 SSL 验证。这 如果使用注释或存根运行器,则在测试中自动发生。如果使用 JUnit 方法,则还需要添加注释,如以下示例所示:​​RestTemplateBuilder​​​​@AutoConfigureWireMock​​​​@Rule​​​​@AutoConfigureHttpClient​

@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {

@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().httpsPort(6443));
...
}

如果您使用,则在 类路径,并且它被选择并配置为忽略SSL 错误。如果使用默认客户端,则不需要注释(但它 没有伤害)。目前不支持其他客户端,但可以添加 在将来的版本中。​​spring-boot-starter-test​​​​RestTemplateBuilder​​​​java.net​

若要禁用自定义,请将属性设置为。​​RestTemplateBuilder​​​​wiremock.rest-template-ssl-enabled​​​​false​

6.5. WireMock 和 Spring MVC Mocks

Spring Cloud Contract 提供了一个方便的类,可以将 JSON WireMock 存根加载到 一个春天。以下代码显示了一个示例:​​MockRestServiceServer​

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class WiremockForDocsMockServerApplicationTests {

@Autowired
private RestTemplate restTemplate;

@Autowired
private Service service;

@Test
public void contextLoads() throws Exception {
// will read stubs classpath
MockRestServiceServer server = WireMockRestServiceServer.with(this.restTemplate)
.baseUrl("https://example.org").stubs("classpath:/stubs/resource.json")
.build();
// We're asserting if WireMock responded properly
assertThat(this.service.go()).isEqualTo("Hello World");
server.verify();
}

}

该值附加到所有模拟调用的前面,该方法采用存根 路径资源模式作为参数。在前面的示例中,定义的存根 atis 加载到模拟服务器中。如果被要求 访问,它会将响应作为在该 URL 上声明。更多 可以指定一个以上的存根模式,每个模式都可以是一个目录(对于递归 全部列表)、固定文件名(如前面的示例所示)或 Ant 样式 模式。JSON格式是普通的WireMock格式,您可以在WireMock网站上阅读。​​baseUrl​​​​stubs()​​​​/stubs/resource.json​​​​RestTemplate​​​​example.org/​​​​.json​

目前,Spring Cloud 合约验证器支持 Tomcat、Jetty 和 Undertow 作为 Spring Boot嵌入式服务器,Wiremock本身对特定的“本机”支持。 Jetty 的版本(当前为 9.2)。要使用本机码头,您需要添加本机 连接依赖关系并排除 Spring Boot 容器(如果有的话)。