消費者驅動的契約測試(Consumer-Driven Contracts,簡稱CDC),是指從消費者業務實現的角度出發,驅動出契約,再基於契約,對提供者驗證的一種測試方式。
原本你要測試的話必須啟動相依的服務
透過 Spring Cloud Contract 的實踐之後你不用啟動這麼多服務,只需拿 Stub 來提供測試
簡單說就是先訂好消費者(consumer)要什麼樣的格式與範例資料,再基於這份契約開發生產者(producer),打包時,除了正常版本的應用程式,還會額外產生 Stub 的 jar ,這個 Stub 也是基於當初的契約來產生能夠透過 Http 回覆簡易資料的啟動器
來看一下 生產者(producer) 的 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>customer-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
<baseClassForTests>com.example.demo.TestContract</baseClassForTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
比較要提的是要加入 spring-cloud-starter-contract-verifier 來幫你驗證是否符合契約部分
以及另外自己要加入 spring-cloud-contract-maven-plugin,baseClassForTests 這個就是你要符合契約的那支測試程式
看一下專案架構
首先來看一下所謂生產跟消費間的契約是什麼東西
import org.springframework.cloud.contract.spec.Contract
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
Contract.make {
description "return all customers"
request {
url "/api/customers"
method GET()
}
response {
status 200
headers {
header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
}
body("data": [[id: 1L, name: "sam"], [id: 2L, name: "andy"]])
//body("""{"data":[{"id":1,"name":"sam"},{"id":2,"name":"andy"}]}""")
}
}
契約 採用 groovy 的 DSL 描述,所以非常好閱讀
可以得知 需要透過 /api/customers 取得一個 json 裡面 data 是一個 list
接下來我們就來實現這份契約
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
private Long id;
private String name;
}
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
@Data
public class Page {
private Collection<Customer> data;
}
@RestController
public class CustomerRestController {
@Autowired
private CustomerRepository customerRepository;
@RequestMapping(path = "/api/customers")
public Page getCustomers() {
Page page = new Page();
page.setData(customerRepository.findAll());
return page;
}
}
好了,基本上我們已經實踐契約的內容,接下來透過 verifier 驗證跟產生 Stub ,
也就是透過這支測試程式 com.example.demo.TestContract
import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
public class TestContract {
@Autowired
private CustomerRestController customerRestController;
@MockBean
private CustomerRepository customerRepository;
@Before
public void before() {
Mockito.when(customerRepository.findAll()).thenReturn(
Arrays.asList(new Customer(1L, "sam"), new Customer(2L, "andy")));
RestAssuredMockMvc.standaloneSetup(this.customerRestController);
}
}
然後執行 maven clear install 測試完後就會裝在本機的 Maven 庫
也可以看到產生了兩個 jar
customer-service-0.0.1-SNAPSHOT.jar
customer-service-0.0.1-SNAPSHOT-stubs.jar
想測試 stub.jar 的話可以看 Stub Runner Boot
抓這個 https://dl.bintray.com/marcingrzejszczak/maven/stub-runner-boot-1.1.0.RELEASE.jar
啟動 stub
java -jar stub-runner-boot-1.1.0.RELEASE.jar --stubrunner.ids=com.example:customer-service:+:8866 --stubrunner.workOffline=true
之後你就可以從 http://localhost:8866/api/customers 取得跟契約一致的資料