Stub Runner Core

存根运行器内核会为服务合作者运行存根。将存根视为服务的合约允许您使用存根运行器作为 Consumer-driven Contracts 的实现。

The stub runner core runs stubs for service collaborators. Treating stubs as contracts of services lets you use stub-runner as an implementation of Consumer-driven Contracts.

Stub Runner 允许您自动下载所提供依赖项的 stub(或从类路径中选择那些),为它们启动 WireMock 服务器,并使用适当的 stub 定义为它们提供支持。对于消息传递,将定义特殊的 stub 路由。

Stub Runner lets you automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them, and feed them with proper stub definitions. For messaging, special stub routes are defined.

Retrieving stubs

您可以从以下获取 stub 的选项中进行选择:

You can pick from the following options of acquiring stubs:

  • Aether-based solution that downloads JARs with stubs from Artifactory or Nexus

  • Classpath-scanning solution that searches the classpath with a pattern to retrieve stubs

  • Writing your own implementation of the org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder for full customization

后一个示例在 Custom Stub Runner 部分中进行说明了。

The latter example is described in the Custom Stub Runner section.

Downloading Stubs

您可以使用 stubsMode 开关控制 stub 的下载。它从 StubRunnerProperties.StubsMode 枚举中选择值。您可以使用以下选项:

You can control the downloading of stubs with the stubsMode switch. It picks value from the StubRunnerProperties.StubsMode enumeration. You can use the following options:

  • StubRunnerProperties.StubsMode.CLASSPATH (default value): Picks stubs from the classpath

  • StubRunnerProperties.StubsMode.LOCAL: Picks stubs from a local storage (for example, .m2)

  • StubRunnerProperties.StubsMode.REMOTE: Picks stubs from a remote location

以下示例从本地位置选择 stub:

The following example picks stubs from a local location:

@AutoConfigureStubRunner(repositoryRoot="https://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)

Classpath scanning

如果您将 stubsMode 属性设置为 StubRunnerProperties.StubsMode.CLASSPATH(或不设置任何内容,因为 CLASSPATH 是默认值),则扫描类路径。考虑以下示例:

If you set the stubsMode property to StubRunnerProperties.StubsMode.CLASSPATH (or set nothing since CLASSPATH is the default value), the classpath is scanned. Consider the following example:

@AutoConfigureStubRunner(ids = {
    "com.example:beer-api-producer:+:stubs:8095",
    "com.example.foo:bar:1.0.0:superstubs:8096"
})

您可以将依赖项添加到类路径,如下所示:

You can add the dependencies to your classpath, as follows:

Maven
<dependency>
    <groupId>com.example</groupId>
    <artifactId>beer-api-producer-restdocs</artifactId>
    <classifier>stubs</classifier>
    <version>0.0.1-SNAPSHOT</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>*</groupId>
            <artifactId>*</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.example.thing1</groupId>
    <artifactId>thing2</artifactId>
    <classifier>superstubs</classifier>
    <version>1.0.0</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>*</groupId>
            <artifactId>*</artifactId>
        </exclusion>
    </exclusions>
</dependency>
Gradle
testCompile("com.example:beer-api-producer-restdocs:0.0.1-SNAPSHOT:stubs") {
    transitive = false
}
testCompile("com.example.thing1:thing2:1.0.0:superstubs") {
    transitive = false
}

然后,扫描类路径上的指定位置。对于 com.example:beer-api-producer-restdocs,扫描以下位置:

Then the specified locations on your classpath get scanned. For com.example:beer-api-producer-restdocs, the following locations are scanned:

  • /META-INF/com.example/beer-api-producer-restdocs/*/.*

  • /contracts/com.example/beer-api-producer-restdocs/*/.*

  • /mappings/com.example/beer-api-producer-restdocs/*/.*

对于 com.example.thing1:thing2,扫描以下位置:

For com.example.thing1:thing2, the following locations are scanned:

  • /META-INF/com.example.thing1/thing2/*/.*

  • /contracts/com.example.thing1/thing2/*/.*

  • /mappings/com.example.thing1/thing2/*/.*

在你打包producer stub时,必须明确提供组和artifact id。

You have to explicitly provide the group and artifact IDs when you package the producer stubs.

为实现适当的 stub 打包,生产者将按照如下方式设置合同:

To achieve proper stub packaging, the producer would set up the contracts as follows:

└── src
    └── test
        └── resources
            └── contracts
                └── com.example
                    └── beer-api-producer-restdocs
                        └── nested
                            └── contract3.groovy

通过使用 Maven assembly pluginGradle Jar 任务,您必须在自己的存根 jar 中创建以下结构:

By using the Maven assembly plugin or the Gradle Jar task, you have to create the following structure in your stubs jar:

└── META-INF
    └── com.example
        └── beer-api-producer-restdocs
            └── 2.0.0
                ├── contracts
                │   └── nested
                │       └── contract2.groovy
                └── mappings
                    └── mapping.json

通过维护此结构,将扫描类路径并且无需下载工件即可从消息传递或 HTTP 存根中获益。

By maintaining this structure, the classpath gets scanned and you can profit from the messaging or HTTP stubs without the need to download artifacts.

Configuring HTTP Server Stubs

Stub Runner 有一个 HttpServerStub 的概念,它抽象了底层 HTTP 服务器的具体实现(例如,WireMock 是实现之一)。有时,您需要对存根服务器执行一些附加的调整(这对给定的实现是具体的)。要做到这一点,Stub Runner 为您提供了 httpServerStubConfigurer 属性,该属性在注释和 JUnit 规则中可用,并且可以通过系统属性进行访问,您可以在其中提供 org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer 接口的实现。这些实现可以更改给定 HTTP 服务器存根的配置文件。

Stub Runner has a notion of a HttpServerStub that abstracts the underlying concrete implementation of the HTTP server (for example, WireMock is one of the implementations). Sometimes, you need to perform some additional tuning (which is concrete for the given implementation) of the stub servers. To do that, Stub Runner gives you the httpServerStubConfigurer property that is available in the annotation and the JUnit rule and is accessible through system properties, where you can provide your implementation of the org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer interface. The implementations can alter the configuration files for the given HTTP server stub.

Spring Cloud Contract Stub Runner 带有一个可以针对 WireMock 扩展的实现:org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStubConfigurer。在 configure 方法中,您可以提供给定存根自己的自定义配置。用例可能是针对给定的工件 ID 在 HTTPS 端口上启动 WireMock。以下示例演示如何执行此操作:

Spring Cloud Contract Stub Runner comes with an implementation that you can extend for WireMock: org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStubConfigurer. In the configure method, you can provide your own custom configuration for the given stub. The use case might be starting WireMock for the given artifact ID, on an HTTPS port. The following example shows how to do so:

Example 1. WireMockHttpServerStubConfigurer implementation
Unresolved directive in stub-runner-core.adoc - include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[]

然后,您可以使用 @AutoConfigureStubRunner 注释对其进行重复使用,如下所示:

You can then reuse it with the @AutoConfigureStubRunner annotation, as follows:

Unresolved directive in stub-runner-core.adoc - include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[]

每当找到 HTTPS 端口时,它都优先于 HTTP 端口。

Whenever an HTTPS port is found, it takes precedence over the HTTP port.

Running stubs

本节介绍了如何运行存根。它包含以下主题:

This section describes how to run stubs. It contains the following topics:

HTTP Stubs

存根使用 JSON 文档进行定义,其语法由 WireMock documentation 中定义。

Stubs are defined in JSON documents, whose syntax is defined in the WireMock documentation.

以下示例在 JSON 中定义了一个存根:

The following example defines a stub in JSON:

{
    "request": {
        "method": "GET",
        "url": "/ping"
    },
    "response": {
        "status": 200,
        "body": "pong",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
}

Viewing Registered Mappings

每个存根的合作者都会在 __/admin/ 端点下公开一个已定义的映射列表。

Every stubbed collaborator exposes a list of defined mappings under the __/admin/ endpoint.

您还可以使用 mappingsOutputFolder 属性将映射转储到文件中。对于基于注释的方法,它类似于以下示例:

You can also use the mappingsOutputFolder property to dump the mappings to files. For the annotation-based approach, it would resembling the following example:

@AutoConfigureStubRunner(ids="a.b.c:loanIssuance,a.b.c:fraudDetectionServer",
mappingsOutputFolder = "target/outputmappings/")

对于 JUnit 方法,它类似于以下示例:

For the JUnit approach, it resembles the following example:

@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
			.repoRoot("https://some_url")
			.downloadStub("a.b.c", "loanIssuance")
			.downloadStub("a.b.c:fraudDetectionServer")
			.withMappingsOutputFolder("target/outputmappings")

然后,如果您签出 target/outputmappings 文件夹,您将看到以下结构;

Then, if you check out the target/outputmappings folder, you would see the following structure;

.
├── fraudDetectionServer_13705
└── loanIssuance_12255

这意味着注册了两个存根。fraudDetectionServer 在端口 13705 注册,loanIssuance 在端口 12255 注册。如果我们查看其中一个文件,我们将看到(对于 WireMock)给定服务器可用的映射:

That means that there were two stubs registered. fraudDetectionServer was registered at port 13705 and loanIssuance at port 12255. If we take a look at one of the files, we would see (for WireMock) the mappings available for the given server:

[{
  "id" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7",
  "request" : {
    "url" : "/name",
    "method" : "GET"
  },
  "response" : {
    "status" : 200,
    "body" : "fraudDetectionServer"
  },
  "uuid" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7"
},
...
]

Messaging Stubs

根据提供的 Stub Runner 依赖和 DSL,消息传递路由会自动设置。

Depending on the provided Stub Runner dependency and the DSL, the messaging routes are automatically set up.