Spring Cloud Contract 是一个包含解决方案的总括项目,可帮助用户成功实施消费者驱动的合同方法。目前 Spring Cloud Contract 由 Spring Cloud Contract Verifier 项目组成。
Spring Cloud Contract Verifier 是一种工具,它支持基于 JVM 的应用程序的消费者驱动契约 (CDC) 开发。它附带了用 Groovy 或 YAML 编写的合同定义语言 (DSL)。
简单点来说就是提前设置一个已知结果,消费者调用时直接返回该结果,该结果在消费者的程序处理中是否正确,来测试消费者程序。
目前很多系统都采用了微服务的开发模式,随着项目规模的不断扩大,服务数量越来越多,服务之间的依赖也越来越密切。不同的团队负责不同的服务开发,其中某个服务发生变动,很多服务也是需要进行修改,这将需要大量的沟通成本和代码修改时间成本。
单元测试无法测试服务间的连接或接口调用正确性。
API接口测试是针对业务接口进行,主要是测试接口实现是否完整,且需要依赖具体真实服务。
集成测试无疑是最佳的,但是需要依赖具体真实服务,且各个服务都需要同时进行测试,其效率很慢。
契约测试,无需依赖具体真实服务,测试服务间的连接或接口调用正确性 。测试效率快。
总体来说,契约测试是一个介于单元测试和集成测试的一个阶段,他关注的细粒度比单元测试更粗,但是又无法取代集成测试。
目前官网和很多博客文章都介绍了,远程库使用契约测试的方法。我这边就介绍一下本地库实现方法。基本一致,只不过读取jar是读取本地。
在服务端编写一个简单的接口,判断数字是否为负数。
- @RestController
- public class NumberController {
-
- @GetMapping("/value/number")
- public String isMinus(@RequestParam("number") Integer number) {
- return number < 0 ? "true" : "false";
- }
- }
pom文件
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-dependenciesartifactId>
- <version>Hoxton.SR12version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-contract-verifierartifactId>
- <version>3.1.1version>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <scope>testscope>
- dependency>
- dependencies>
-
- <build>
- <resources>
- <resource>
- <directory>src/main/resourcesdirectory>
- <filtering>truefiltering>
- resource>
- resources>
-
- <plugins>
-
- <plugin>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-contract-maven-pluginartifactId>
- <version>3.1.1version>
- <extensions>trueextensions>
- <configuration>
- <testFramework>JUNITtestFramework>
- <baseClassForTests>com.lin.test.BaseTestClassbaseClassForTests>
- configuration>
- plugin>
-
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- <version>2.3.12.RELEASEversion>
- <configuration>
- <mainClass> com.lin.test.MainApplicationmainClass>
- configuration>
- <executions>
- <execution>
- <goals>
- <goal>repackagegoal>
- goals>
- execution>
- executions>
- plugin>
- plugins>
- build>
接下来需要新建基础测试类以及存根,注意存放目录,如图所示:

测试类
- @RunWith(SpringRunner.class)
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
- @DirtiesContext
- @AutoConfigureMessageVerifier
- public class BaseTestClass {
- @Autowired
- private NumberController numberController;
-
- @Before
- public void setup() {
- StandaloneMockMvcBuilder standaloneMockMvcBuilder = MockMvcBuilders.standaloneSetup(numberController);
- RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
- }
- }
存根(groovy文件)
- import org.springframework.cloud.contract.spec.Contract
-
- Contract.make {
- description "should return true when number input is minus"
- request {
- method GET()
- url("/value/number") {
- queryParameters {
- parameter("number", "-1")
- }
- }
- }
- response {
- body("true")
- status 200
- }
- }
- import org.springframework.cloud.contract.spec.Contract
-
- Contract.make {
- description "should return false when number input not minus"
- request {
- method GET()
- url("/value/number") {
- queryParameters {
- parameter("number", "1")
- }
- }
- }
- response {
- body("false")
- status 200
- }
- }
到此服务提供者编写完成,我们接下来进行mvn clean install 就会生成测试类

构建还会在我们的本地Maven存储库中添加存根jar。

- @RestController
- public class BasicMathController {
- @Autowired
- private RestTemplate restTemplate;
-
- @GetMapping("/calculate")
- public String checkOddAndEven(@RequestParam("number") Integer number) {
- HttpHeaders httpHeaders = new HttpHeaders();
- httpHeaders.add("Content-Type", "application/json");
-
- ResponseEntity
responseEntity = restTemplate.exchange( - "http://localhost:8090/value/number?number=" + number, HttpMethod.GET,
- new HttpEntity<>(httpHeaders), String.class);
-
- return responseEntity.getBody();
- }
- }
pom文件
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-dependenciesartifactId>
- <version>Hoxton.SR12version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-contract-wiremockartifactId>
- <version>3.1.1version>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-contract-stub-runnerartifactId>
- <version>3.1.1version>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-data-restartifactId>
- dependency>
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>org.examplegroupId>
- <artifactId>provider-testartifactId>
- <version>1.0-SNAPSHOTversion>
- dependency>
- dependencies>
-
- <build>
- <resources>
- <resource>
- <directory>src/main/resourcesdirectory>
- <filtering>truefiltering>
- resource>
- resources>
-
- <plugins>
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- <version>2.3.12.RELEASEversion>
- <configuration>
-
- <mainClass> com.lin.test.MainApplicationmainClass>
- configuration>
- <executions>
- <execution>
- <goals>
- <goal>repackagegoal>
- goals>
- execution>
- executions>
- plugin>
- plugins>
- build>
存根运行器
- @RunWith(SpringRunner.class)
- @SpringBootTest(webEnvironment = WebEnvironment.MOCK)
- @AutoConfigureMockMvc
- @AutoConfigureJsonTesters
- @AutoConfigureStubRunner(ids = {"org.example:provider-test:+:stubs:8090"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
- public class BasicMathControllerIntegrationTest {
-
- @Autowired
- private MockMvc mockMvc;
-
- @Test
- public void given_WhenPassEvenNumberInQueryParam_ThenReturnEven() throws Exception {
- mockMvc.perform(MockMvcRequestBuilders.get("/calculate?number=2").contentType(MediaType.APPLICATION_JSON))
- .andExpect(status().isOk()).andExpect(content().string("Even"));
- }
- }
@AutoConfigureStubRunner这个注解非常重要,这里选择的是本地存根。 之前以为这样就完事了,调用时报错了。找不到对应maven存储库,找到了默认的。

需要进行设置参数:
org.apache.maven.user-settings
![]()

到此结束,文章有些粗糙,有哪里不对的,欢迎指正。