在生產者端使用契約進行消費者驅動契約 (CDC) 的分步指南
考慮一個欺詐檢測和貸款發放過程的示例。業務場景是:我們想向人們發放貸款,但又不希望他們從我們這裡騙取錢財。我們系統的當前實現是向所有人發放貸款。
假設貸款發放是欺詐檢測伺服器的客戶端。在當前衝刺中,我們必須開發一個新功能:如果客戶想借的錢太多,我們就將該客戶標記為欺詐。
技術說明
-
欺詐檢測的
artifact-id是http-server。 -
貸款發放的
artifact-id是http-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
消費者端(貸款發放)
作為貸款發放服務(欺詐檢測伺服器的消費者)的開發者,您可以執行以下步驟
-
透過為您的功能編寫測試來開始 TDD。
-
編寫缺失的實現。
-
在本地克隆欺詐檢測服務倉庫。
-
在欺詐檢測服務的倉庫中本地定義契約。
-
新增 Spring Cloud Contract (SCC) 外掛。
-
執行整合測試。
-
提交拉取請求。
-
建立初始實現。
-
接管拉取請求。
-
編寫缺失的實現。
-
部署您的應用程式。
-
線上工作。
我們從貸款發放流程開始,以下 UML 圖顯示了該流程
編寫缺失的實現
在某個時刻,您需要向欺詐檢測服務傳送請求。假設您需要傳送包含客戶 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欄位的值為FRAUD,rejectionReason欄位的值為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 自動下載存根的功能,您必須在您的消費者端專案(貸款申請服務)中執行以下操作
-
新增
Spring Cloud ContractBOM,如下所示 -
新增
Spring Cloud Contract Stub Runner的依賴項,如下所示 -
使用
@AutoConfigureStubRunner註解您的測試類。在註解中,為 Stub Runner 提供group-id和artifact-id,以便下載您的協作者的存根。 -
(可選)因為您正在離線與協作者一起玩,您還可以提供離線工作開關(
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 圖顯示了欺詐檢測流程
接管拉取請求
作為提醒,以下清單顯示了初始實現
然後您可以執行以下命令
$ 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,同樣適用於 response 的 matchers 部分。
請注意,在生產者端,您也在進行 TDD。期望以測試的形式表達。此測試向我們自己的應用程式傳送一個請求,其中包含契約中定義的 URL、標頭和正文。它還期望響應中包含精確定義的值。換句話說,您擁有 red、green 和 refactor 中的 red 部分。現在是時候將 red 轉換為 green 了。