上一篇中我们介绍了微服务架构中消费者驱动契约测试基本理念和方法,今天我们将把这些理念和方法付诸于实践。
在以Spring Cloud作为微服务基础架构的开发环境中,集成Spring Cloud Contracts作为消费者驱动契约测试工具是最佳选择。Spring Cloud Contracts中提供了Spring Cloud Contracts Verifier和Stub Runner等核心组件,这些组件使得基于JVM的消费者驱动契约的开发成为可能。
Spring Cloud Contracts的设计思想和目的在于生成验收测试和提供快速测试反馈,不需要真正启动所依赖的其他所有服务就能完成服务契约的正确性验证(如下图所示)。Contracts Verifier和Stub Runner组件可以确保能够正确Mock服务端的接口,并在发生契约变化时提供一种可立即被服务端和消费端发现的方式。
在本节中,我们将基于Order服务与Account服务之间的调用关系来讨论如何基于Spring Cloud Contracts实现面向契约的端对端测试。我们对两个服务之间的交互做了简化以便更好的演示技术细节。
1. 初始化环境
从业务场景上讲,Account服务相当于服务的提供者,而Order服务是Account服务的消费者。无论是服务的提供者还是消费者,都需要导入以下关于Spring Cloud Contracts 的maven依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-wiremock</artifactId>
<scope>test</scope>
</dependency>
对于Account服务而言,我们按照一般的开发流程分别提供了AccountRepository和AccountController实现,其中AccountController的代码结构如下所示(为了演示的简单性去除了Service层)。
@RestController
public class AccountController {
@Autowired
private AccountRepository repository;
@RequestMapping(path = "/v1/accounts")
public AccountData getCustomers() {
AccountData data = new AccountData();
data.setData(repository.findAll());
return data;
}
}
现在准备工作已经就绪,但Spring Cloud Contracts实现面向契约的端对端测试的流程比较复杂(见下图),我们需要对其中的每一步都展开讨论。
2. 制定服务契约
引入Spring Cloud Contracts Verifier之后我们就可以使用该组件来定义契约。SpringCloud Contracts Verifier提供了契约定义语言(Contract Definition Language,CDL)用来定义与契约相关的资源。
Spring Cloud Contracts中的契约是采用groovy的DSL进行描述,案例中的shouldReturnAllAccounts.groovy契约文件内容如下所示。
import org.springframework.cloud.contract.spec.Contract
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
Contract.make {
description "return all accounts"
request {
url "/v1/accounts"
method GET()
}
response {
status 200
headers {
header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
}
body("data": [[id: 1L, username: "tianyalan1", firstName: "tianmin", lastName: "zheng", email: "[email protected]"],
[id: 2L, username: "tianyalan2", firstName: "tianmin", lastName: "zheng", email: "[email protected]"]])
}
}
我们看到以上契约文件中包含三个部分,即description、request和response。通过request定义了请求时的url和method,然后通过response约定返回时的headers和body信息。该契约描述的语义也一目了然,就是通过/v1/accounts这个url来获取一个JSON格式的account列表,该列表将返回两个用户账户信息。请注意我们需要在test/resources目录下新建一个contracts文件夹,这个是放置该契约文件的地方。
3. 生成Stub文件
定义完契约文件之后,接下来我们就可以生成stub文件。Stub文件在表现形式上也是一个jar包,这个jar包的目的就是可以被消费者拿来当做一个模拟服务进行启动然后在本地运行测试用例,而不需要服务提供者真正启动服务。
我们首先需要引入spring-cloud-contract-maven-plugin插件,spring-cloud-contract-maven-plugin插件的使用方式如下,该插件在maven打包过程中会自动创建stub文件。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>1.1.4.RELEASE</version>
<extensions>true</extensions>
<configuration>
<packageForbaseClasses>
com.tianyalan.testing.cdc.accounts
</packageForbaseClasses>
</configuration>
</plugin>
</plugins>
</build>
现在我们通过mvn install –DskipTests=true命令打包account-service工程,除了普通的日志输出之外,控制台还会生成如下信息(为了显示简单,去掉了文件路径等无用信息)。
[INFO] Copying file shouldReturnAllAccounts.groovy
[INFO] Converting from Spring Cloud Contract Verifier contracts to WireMock stubs mappings
[INFO] Spring Cloud Contract Verifier contracts directory: …\account-service-cdc\src\test\resources\contracts
[INFO] WireMock stubs mappings directory: …\account-service-cdc\target\stubs\META-INF\com.tianyalan.testing.cdc\account-service-cdc\0.0.1-SNAPSHOT\mappings
[INFO] Creating new stub […\account-service-cdc\target\stubs\META-INF\com.tianyalan.testing.cdc\account-service-cdc\0.0.1-SNAPSHOT\mappings\shouldReturnAllAccounts.json]
…
Installing …\account-service-cdc\target\account-service-cdc-0.0.1-SNAPSHOT-stubs.jar to C:\Users\Tianmin.Zheng\.m2\repository\com\tianyalan\testing\cdc\account-service-cdc\0.0.1-SNAPSHOT\account-service-cdc-0.0.1-SNAPSHOT-stubs.jar
根据这些日志信息,我们看到打包过程对shouldReturnAllAccounts.groovy契约文件做了处理。打包完成之后,在target目录下会生成两个jar包,一个是正常的account-service-cdc-0.0.1-SNAPSHOT.jar文件,另一个就是新的stub文件。Stub文件的名称为account-service-cdc-0.0.1-SNAPSHOT-stubs.jar,打开该文件会发现两个文件夹,一个是contracts文件夹,内部存放着shouldReturnAllAccounts.groovy契约文件,另一个是mappings文件夹,内部存放着shouldReturnAllAccounts.json文件,shouldReturnAllAccounts.json文件是用JSON格式对shouldReturnAllAccounts.groovy契约文件的一种数据转换。
生成stub文件之后,我们还需要做的事情是通过install命令将该stub文件上传到maven仓库,以便消费者通过pom中定义的group-id和artifact-id加载该jar包。至此,服务提供者的开发工作告一段落。下一篇我们将介绍如何编写测试用例并执行测试。
本文源代码可以在笔者的github上进行下载:https://github.com/tianminzheng/microservice-testing-cdc。
更多内容可以关注我的公众号: