在生产者端使用契约进行消费者驱动契约 (CDC) 的分步指南

考虑一个欺诈检测和贷款发放过程的示例。业务场景是:我们想向人们发放贷款,但又不希望他们从我们这里骗取钱财。我们系统的当前实现是向所有人发放贷款。

假设贷款发放欺诈检测服务器的客户端。在当前冲刺中,我们必须开发一个新功能:如果客户想借的钱太多,我们就将该客户标记为欺诈。

技术说明

  • 欺诈检测的artifact-idhttp-server

  • 贷款发放的artifact-idhttp-client

  • 两者都有一个group-id,即com.example

  • 为了这个例子,存根存储是 Nexus/Artifactory。

社会说明

  • 客户端和服务器开发团队都需要直接沟通,并在过程中讨论变更。

  • CDC 核心在于沟通。

服务器端代码可在 Spring Cloud Contract Samples 仓库的 samples/standalone/dsl/http-server 路径下找到,客户端代码可在 Spring Cloud Contract 仓库的 samples/standalone/dsl/http-client 路径下找到。

在这种情况下,生产者拥有契约。实际上,所有契约都在生产者的仓库中。

技术说明

重要提示:所有代码都可在 Spring Cloud Contract Samples 仓库中找到。

为简洁起见,我们使用以下缩写

  • 贷款发放 (LI):HTTP 客户端

  • 欺诈检测 (FD):HTTP 服务器

  • SCC:Spring Cloud Contract

消费者端(贷款发放)

作为贷款发放服务(欺诈检测服务器的消费者)的开发者,您可以执行以下步骤

  1. 通过为您的功能编写测试来开始 TDD。

  2. 编写缺失的实现。

  3. 在本地克隆欺诈检测服务仓库。

  4. 在欺诈检测服务的仓库中本地定义契约。

  5. 添加 Spring Cloud Contract (SCC) 插件。

  6. 运行集成测试。

  7. 提交拉取请求。

  8. 创建初始实现。

  9. 接管拉取请求。

  10. 编写缺失的实现。

  11. 部署您的应用程序。

  12. 在线工作。

我们从贷款发放流程开始,以下 UML 图显示了该流程

getting-started-cdc-client

通过为您的功能编写测试来开始 TDD

以下清单显示了我们可能用来检查贷款金额是否过大的测试

假设您已经编写了新功能的测试。如果收到的贷款申请金额过大,系统应该拒绝该贷款申请并附带一些描述。

编写缺失的实现

在某个时刻,您需要向欺诈检测服务发送请求。假设您需要发送包含客户 ID 和客户想要借款金额的请求。您想通过 PUT 方法将其发送到 /fraudcheck URL。为此,您可以使用类似于以下代码的代码

为简单起见,欺诈检测服务的端口设置为 8080,应用程序运行在 8090

如果此时您启动测试,它会中断,因为目前没有服务在端口 8080 上运行。

在本地克隆欺诈检测服务仓库

您可以从尝试服务器端契约开始。为此,您必须首先通过运行以下命令来克隆它

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

在欺诈检测服务的仓库中本地定义契约

作为消费者,您需要明确定义您想要实现的目标。您需要阐明您的期望。为此,请编写以下契约

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

以下示例显示了我们的契约,包括 Groovy 和 YAML 格式

YML 契约非常直截了当。然而,当您查看使用静态类型 Groovy DSL 编写的契约时,您可能会想 value(client(…​), server(…​)) 部分是什么。通过使用这种表示法,Spring Cloud Contract 允许您定义 JSON 块、URL 或其他动态结构的一部分。在标识符或时间戳的情况下,您无需硬编码值。您希望允许一些不同范围的值。要启用值范围,您可以设置与消费者侧这些值匹配的正则表达式。您可以通过映射表示法或带插值的字符串提供正文。我们强烈建议使用映射表示法。

要设置契约,您必须了解映射表示法。请参阅 Groovy 关于 JSON 的文档

前面显示的契约是双方之间的协议,即

  • 如果发送 HTTP 请求时包含所有以下内容

    • /fraudcheck 端点的 PUT 方法

    • 一个 JSON 主体,其中 client.id 匹配正则表达式 [0-9]{10}loanAmount 等于 99999

    • Content-Type 标头,值为 application/vnd.fraud.v1+json

  • 则会向消费者发送一个 HTTP 响应,该响应

    • 状态为 200

    • 包含一个 JSON 主体,其中 fraudCheckStatus 字段的值为 FRAUDrejectionReason 字段的值为 Amount too high

    • Content-Type 标头,值为 application/vnd.fraud.v1+json

一旦您准备好在集成测试中实际检查 API,您需要在本地安装存根。

添加 Spring Cloud Contract Verifier 插件

我们可以添加 Maven 或 Gradle 插件。在此示例中,我们展示如何添加 Maven。首先,我们添加 Spring Cloud Contract BOM,示例如下

接下来,添加 Spring Cloud Contract Verifier Maven 插件,示例如下

由于插件已添加,您将获得 Spring Cloud Contract Verifier 的功能,这些功能根据提供的契约

  • 生成并运行测试

  • 生成并安装存根

您不想生成测试,因为作为消费者,您只想使用存根。您需要跳过测试生成和调用。为此,请运行以下命令

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

运行这些命令后,您应该在日志中看到类似以下内容

[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

以下这行非常重要

[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 Cloud Contract Stub Runner 自动下载存根的功能,您必须在您的消费者端项目(贷款申请服务)中执行以下操作

  1. 添加 Spring Cloud Contract BOM,如下所示

  2. 添加 Spring Cloud Contract Stub Runner 的依赖项,如下所示

  3. 使用 @AutoConfigureStubRunner 注解您的测试类。在注解中,为 Stub Runner 提供 group-idartifact-id,以便下载您的协作者的存根。

  4. (可选)因为您正在离线与协作者一起玩,您还可以提供离线工作开关(StubRunnerProperties.StubsMode.LOCAL)。

现在,当您运行测试时,您会在日志中看到类似以下输出

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}]

此输出意味着 Stub Runner 找到了您的存根,并为您的应用程序启动了一个服务器,其 group ID 为 com.example,artifact ID 为 http-server,存根版本为 0.0.1-SNAPSHOT,分类器为 stubs,端口为 8080

提交拉取请求

到目前为止您所做的是一个迭代过程。您可以反复尝试契约,在本地安装它,并在消费者端进行工作,直到契约按您希望的方式运行。

一旦您对结果满意且测试通过,您就可以向服务器端发布拉取请求。目前,消费者端的工作已完成。

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

作为欺诈检测服务器(贷款发放服务的服务器)的开发者,您可能希望

  • 接管拉取请求

  • 编写缺失的实现

  • 部署应用程序

以下 UML 图显示了欺诈检测流程

getting-started-cdc-server

接管拉取请求

作为提醒,以下清单显示了初始实现

然后您可以运行以下命令

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

您必须添加自动生成的测试所需的依赖项,如下所示

在 Maven 插件的配置中,您必须传递 packageWithBaseClasses 属性,如下所示

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

所有生成的测试都扩展该类。在那里,您可以设置您的 Spring 上下文或任何必要的东西。在这种情况下,您应该使用 Rest Assured MVC 来启动服务器端 FraudDetectionController。以下清单显示了 FraudBase

现在,如果您运行 ./mvnw clean install,您会得到类似以下的输出

Results :

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

此错误发生是因为您有一个新的契约,从中生成了一个测试,并且由于您尚未实现该功能,该测试失败了。自动生成的测试将类似于以下测试方法

@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,您会看到契约中 value(consumer(…​), producer(…​)) 块中所有 producer() 部分都被注入到测试中。如果您使用 YAML,同样适用于 responsematchers 部分。

请注意,在生产者端,您也在进行 TDD。期望以测试的形式表达。此测试向我们自己的应用程序发送一个请求,其中包含契约中定义的 URL、标头和正文。它还期望响应中包含精确定义的值。换句话说,您拥有 redgreenrefactor 中的 red 部分。现在是时候将 red 转换为 green 了。

编写缺失的实现

由于您知道预期的输入和输出,您可以按如下方式编写缺失的实现

当您再次运行 ./mvnw clean install 时,测试通过。由于 Spring Cloud Contract Verifier 插件将测试添加到 generated-test-sources,您实际上可以从 IDE 中运行这些测试。

部署您的应用程序

完成工作后,您可以部署更改。为此,您必须首先通过运行以下命令合并分支

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

您的 CI 可能会运行类似 ./mvnw clean deploy 的命令,该命令将发布应用程序和存根工件。

消费者端(贷款发放),最后一步

作为贷款发放服务(欺诈检测服务器的消费者)的开发者,您需要

  • 将我们的功能分支合并到 master

  • 切换到在线工作模式

以下 UML 图显示了流程的最终状态

getting-started-cdc-client-final

将分支合并到主分支

以下命令显示了使用 Git 将分支合并到主分支的一种方式

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

在线工作

现在您可以禁用 Spring Cloud Contract Stub Runner 的离线工作,并指出存根仓库的位置。此时,服务器端的存根会自动从 Nexus/Artifactory 下载。您可以将 stubsMode 的值设置为 REMOTE。以下代码显示了通过更改属性实现相同目标的示例

就是这样。您已经完成了本教程。

© . This site is unofficial and not affiliated with VMware.