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

消费者驱动契约(CDC)逐步指南:在生产者端使用契约

考虑一个欺诈检测和贷款发放流程的例子。业务场景是,我们希望向人们发放贷款,但不希望他们从我们这里窃取资金。当前系统的实现方式是向所有人发放贷款。spring-doc.cadn.net.cn

假设 Loan IssuanceFraud Detection 服务器的客户端。在当前冲刺中,我们必须开发一项新功能:如果客户申请的贷款金额过高,我们将该客户标记为欺诈者。spring-doc.cadn.net.cn

服务器端代码可在 Spring Cloud Contract Samples 仓库的 samples/standalone/dsl/http-server 路径下获取,客户端代码则可在 Spring Cloud Contract 的仓库 samples/standalone/dsl/http-client 路径下找到。spring-doc.cadn.net.cn

在这种情况下,生产者拥有合同。在物理上,所有的合同都位于生产者的存储库中。

技术说明

重要:所有代码均可在 spring Cloud Contract 示例仓库 中获取。spring-doc.cadn.net.cn

为简化起见,我们使用以下缩写:spring-doc.cadn.net.cn

消费者端(贷款发放)

作为贷款发放服务(欺诈检测服务器的消费者)的开发者,您可能需要执行以下步骤:spring-doc.cadn.net.cn

  1. 通过为您的功能编写测试来开始进行TDD(测试驱动开发)。spring-doc.cadn.net.cn

  2. 编写缺失的实现。spring-doc.cadn.net.cn

  3. 本地克隆欺诈检测服务仓库。spring-doc.cadn.net.cn

  4. define the contract locally in the repository of the fraud detection service.spring-doc.cadn.net.cn

  5. 添加Spring Cloud Contract(SCC)插件。spring-doc.cadn.net.cn

  6. 运行集成测试。spring-doc.cadn.net.cn

  7. 提交Pull Request。spring-doc.cadn.net.cn

  8. 创建一个初始实现。spring-doc.cadn.net.cn

  9. 完成拉取请求。spring-doc.cadn.net.cn

  10. 编写缺失的实现。spring-doc.cadn.net.cn

  11. 部署您的应用程序。spring-doc.cadn.net.cn

  12. 在线工作。spring-doc.cadn.net.cn

我们先来看贷款发放流程,下图显示了该流程的 UML 图:spring-doc.cadn.net.cn

getting-started-cdc-client

开始为你的功能编写测试用例

下面列出了我们可能用来检查贷款金额是否过大的测试代码:spring-doc.cadn.net.cn

假设你已经编写了你新功能的测试。如果收到一笔大金额的贷款申请,系统应该拒绝该贷款申请并附带一些描述。spring-doc.cadn.net.cn

编写缺失的实现

到某个时间点,您需要向欺诈检测服务发送请求。假设需要发送包含客户端 ID 和客户想要借款金额的请求。您要使用PUT方法将它发送到/fraudcheck URL。为此,您可以使用类似的代码:spring-doc.cadn.net.cn

为了简化,欺诈检测服务的端口设置为 8080,并且应用运行在 8090 上。spring-doc.cadn.net.cn

如果您在此处开始测试,将会失败,因为端口8080上没有任何服务当前正在运行。

在本地克隆反欺诈服务仓库

你可以先从使用服务端契约开始。为此,必须首先 克隆它,通过运行以下命令:spring-doc.cadn.net.cn

$ git clone https://your-git-server.com/server-side.git local-http-server-repo

<p>在欺诈检测服务的存储库中本地定义合同</p>

作为消费者,您需要明确您想要实现什么目标。您需要提出您的期望。为此,您需要写下以下合同:spring-doc.cadn.net.cn

将合同放在src/test/resources/contracts/fraud文件夹中。fraud文件夹很重要,因为生产者的测试基础类名引用了该文件夹。

以下示例展示了我们的契约,包括Groovy和YAML两种形式:spring-doc.cadn.net.cn

YML 协议很简单。但是,当您查看使用强类型 Groovy DSL 编写的协议时,可能会想知道这些代码 0 部分是什么意思。通过使用这种表示法,Spring Cloud Contract 允许您为 JSON 块、URL 或其他结构定义部分,这些部分是动态的。在这种情况下,标识符或时间戳不需要硬编码值。您需要允许一些不同的值范围。要启用值范围,可以设置匹配这些值的正则表达式,以便在使用者端。您可以使用映射语法或带有插值的字符串提供正文。我们强烈建议使用映射语法。spring-doc.cadn.net.cn

To set up contracts, you must understand the map notation. See theGroovy docs regarding JSON.spring-doc.cadn.net.cn

之前展示的合同是双方之间的协议,规定了:spring-doc.cadn.net.cn

一旦你准备好在集成测试中实践检查API,就需要在本地安装存档。spring-doc.cadn.net.cn

添加Spring CloudContractVerifier插件

我们可以在Maven或Gradle中添加插件。在本示例中,我们将展示如何添加Maven插件。
首先,如下面的示例所示,我们需要添加Spring Cloud Contract BOM:spring-doc.cadn.net.cn

接下来,添加 Spring Cloud Contract Verifier Maven 插件,如下示例所示:spring-doc.cadn.net.cn

自从添加了插件,您就获得了提供的合同提供的Spring Cloud Contract Verifier个功能:spring-doc.cadn.net.cn

你不想生成测试用例,因为你作为消费者只想玩弄桩。你需要跳过测试生成和调用。为此,运行以下命令:spring-doc.cadn.net.cn

$ cd local-http-server-repo
$ ./mvnw clean install -DskipTests

一旦你运行这些命令,你就会在日志中看到如下内容:spring-doc.cadn.net.cn

[INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
[INFO]
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:1.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.jar
[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.pom
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

重要的是:spring-doc.cadn.net.cn

[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

它确认了存档文件夹中的http-server已安装在本地存储库中。spring-doc.cadn.net.cn

运行集成测试

为了从 Spring Cloud Contract Stub Runner 功能中获得自动存档下载的好处,您必须在消费方侧项目(0)中执行以下操作:Loan Application service:spring-doc.cadn.net.cn

  1. Add the Spring Cloud Contract BOM, as follows:spring-doc.cadn.net.cn

  2. Add the dependency to Spring Cloud Contract Stub Runner, as follows:spring-doc.cadn.net.cn

  3. 使用注解标注您的测试类 @AutoConfigureStubRunner。在注解中提供 group-idartifact-id,以便 Stub Runner 下载您协同对象的占位程序。spring-doc.cadn.net.cn

  4. (可选) 因为您离线与合作者一起玩,所以也可以提供离线工作开关 (0)。spring-doc.cadn.net.cn

现在,当你运行你的测试时,你将在日志中看到如下输出:spring-doc.cadn.net.cn

2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
2016-07-19 14:22:25.438  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved version is 0.0.1-SNAPSHOT
2016-07-19 14:22:25.439  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories []
2016-07-19 14:22:25.451  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
2016-07-19 14:22:25.465  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar]
2016-07-19 14:22:25.475  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
2016-07-19 14:22:27.737  INFO 41050 --- [           main] o.s.c.c.stubrunner.StubRunnerExecutor    : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]

This output means that Stub Runner has found your stubs and started a server for your application with a group ID of com.example and an artifact ID of http-server with version 0.0.1-SNAPSHOT of the stubs and with the stubs classifier on port 8080.spring-doc.cadn.net.cn

提交拉取请求

您到目前为止所做的就是一个迭代过程。您可以围绕契约进行尝试,将其安装在本地,并在消费者一侧进行工作,直到契约按照您的期望工作。spring-doc.cadn.net.cn

一旦对结果满意并且测试通过,就可以向服务器端提交pull请求。目前,消费端的工作已经完成。spring-doc.cadn.net.cn

生产者端(欺诈检测服务器)

作为贷款发放服务的开发人员,您可能希望执行以下操作:spring-doc.cadn.net.cn

以下UML图展示了欺诈检测流程:spring-doc.cadn.net.cn

getting-started-cdc-server

接管拉取请求

作为提醒,下表显示了初始实现。spring-doc.cadn.net.cn

然后,您可以运行以下命令:spring-doc.cadn.net.cn

$ git checkout -b contract-change-pr master
$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr

您必须添加自动生成测试所需的依赖项,如下所示:spring-doc.cadn.net.cn

在Maven插件的配置中,必须传递packageWithBaseClasses属性,如下:spring-doc.cadn.net.cn

本示例通过设置属性packageWithBaseClasses来使用“基于约定”的命名。这样做意味着最后两个包组合起来构成了基础测试类的名称。在我们的例子中,合同被放在了src/test/resources/contracts/fraud文件夹下。由于您从contracts文件夹开始没有两个包,只需选择一个,应该是fraud。添加后缀Base并首字母大写fraud。这就为您提供了FraudBase测试类名。

All the generated tests extend that class. Over there, you can set up your Spring Context or whatever is necessary. In this case, you should use Rest Assured MVC to start the server side FraudDetectionController. The following listing shows the FraudBase class:spring-doc.cadn.net.cn

现在,如果您运行 0,则会得到如下输出:spring-doc.cadn.net.cn

Results :

Tests in error:
  ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed...

此错误发生的原因是,您有一个新的合同,从该合同中生成了测试用例,并且它在您尚未实现该功能时失败。自动生成的测试方法可能如下所示:spring-doc.cadn.net.cn

@Test
public void validate_shouldMarkClientAsFraud() throws Exception {
    // given:
        MockMvcRequestSpecification request = given()
                .header("Content-Type", "application/vnd.fraud.v1+json")
                .body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");

    // when:
        ResponseOptions response = given().spec(request)
                .put("/fraudcheck");

    // then:
        assertThat(response.statusCode()).isEqualTo(200);
        assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
    // and:
        DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
        assertThatJson(parsedJson).field("['fraudCheckStatus']").matches("[A-Z]{5}");
        assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
}

如果您使用Groovy DSL,您会发现Contract中所有出现在producer()块中的value(consumer(…​), producer(…​))部分都注入了测试。如果您使用YAML,则对于matchers部分的response也同样适用。spring-doc.cadn.net.cn

请注意,在生产者一侧,你也进行着TDD。期望以测试的形式表达:该测试会向我们自己的应用程序发送具有在合同中定义的URL、头信息和正文的请求,并期望响应中精确定义的值。换句话说,你已经有了red部分的redgreenrefactor。现在要把red转换为greenspring-doc.cadn.net.cn

编写缺失的实现

因为您知道预期的输入和输出,所以可以按照如下方式编写缺失的实现:spring-doc.cadn.net.cn

当您再次运行./mvnw clean install时,测试将通过。由于Spring Cloud 合同验证器插件会将这些测试添加到您的generated-test-sources中,因此您实际上可以从您的IDE中运行这些测试。spring-doc.cadn.net.cn

部署你的应用程序

完成工作后,您可以部署所做的更改。为此,您必须首先通过运行以下命令合并分支:spring-doc.cadn.net.cn

$ git checkout master
$ git merge --no-ff contract-change-pr
$ git push origin master

你的CI可能会运行一个命令,例如./mvnw clean deploy,这将同时发布应用程序和存根(stub)工件。spring-doc.cadn.net.cn

顾客端(贷款发放),最后一步

作为贷款发放服务的开发者(欺诈检测服务器的消费者),你需要:spring-doc.cadn.net.cn

下面的UML图展示了该过程的最终状态:spring-doc.cadn.net.cn

getting-started-cdc-client-final

Merging a Branch to Master

以下命令展示了一种使用 Git 将分支合并到 master 的方式:spring-doc.cadn.net.cn

$ git checkout master
$ git merge --no-ff contract-change-pr

在线工作

现在,您可以为Spring Cloud Contract Stub Runner禁用离线工作,并指定存放开桩的仓库位置。目前,服务器端的开桩会自动从Nexus/Artifactory下载。可以将stubsMode的值设置为REMOTE。以下代码通过更改属性展示了实现相同功能的方法:spring-doc.cadn.net.cn

教程到此结束。spring-doc.cadn.net.cn