上一篇中我們介紹了微服務架構中消費者驅動契約測試基本理念和方法,今天我們將把這些理念和方法付諸於實踐。
在以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。
更多內容可以關注我的公衆號: