---
# System prepended metadata

title: '第十章 Testing microservices:Part 2'

---

# 第十章 Testing microservices:Part 2
> 微服務架構中的測試策略：Part 2
## 章節大綱
* 上個章節提到的test pyramid
    * unit tests
* 這節會繼續說明上面的三層
    * integration tests
        * 整合測試驗證服務可以正確與基礎設施服務（像是資料庫）和其他app服務互動
    * component tests
        * 使用stubs代替依賴項，來獨立測試服務
    * end-to-end tests
        * 測試所有的服務或是app
## 10.1 整合測試
* API Gateway 使用 REST API
* services 使用 `domain events`
    * 像是 Order History Service 裡面 Order Service 用好幾個其他的服務
* Orders 儲存在 MySQL
* Kitchen Service 對其他服務發送命令還有等待使用他的回覆
![](https://i.imgur.com/8GJ94K5.png)
* 如果把所有東西一起測試就是最後的 end-to-end tests 
    * 太慢 成本高 又容易出問題
* 用Integration tests
    ![](https://i.imgur.com/UmJa2Nw.png)
    * 以下有兩種方法
        * 測試每個服務的配接器(adapters)
            * 直接測試資料庫
        * 使用合約
            * 合約是一對服務之間互動的具體範例
            * 合約的結構取決於服務之間互動的類型
            ![](https://i.imgur.com/gILj0CL.png)
    * consumer-side test - 使用合約來配置可模擬提供程序的stub，來做整合測試 
    * Provider-side tests - 使用合約對adapters的來測試，adapters的依賴項用mock
### 10.1.1 針對持久化層的整合測試
* unit tests 只有在記憶體裡測試
* persistence integration test的每個階段
    * Setup - 創建 database schema 以及初始化
    * Execute - 執行動作
    * Verify - 驗證從資料庫取回的資料 
    * Teardown - 讓資料庫變回本狀態 像是回滾之類的操作
    ![](https://i.imgur.com/IblOnHm.png)
    ![](https://i.imgur.com/sjombc1.png)
* shouldSaveAndLoadOrder()
    * 執行兩個transaction
        * 儲存新的訂單
        * 載入訂單並驗證
* 資料庫的部分可以用Docker來測試
### 10.1.2 針對基於REST的 request/response style interactions 的整合測試
* 使用consumer-driven contract tests
    *  API Gateway 跟 GET /orders/{orderId} 的互動可以用 HTTP-based contracts 來表示
* 使用 Spring Cloud Contract 來測試 REST-based interactions
    * consumer-side API Gateway 整合測試用合約配置的 HTTP stub server 模擬 Order Service 的功能
    * Spring Cloud Contract 用合約 **code-generate** provider-side 的 Order Service 整合測試
        *  去測試**controllers** 
        * 合約對 controller request 特定的 HTTP request 
        * 合約也對 controller 定好特定的 response
![](https://i.imgur.com/PyDNyog.png)
* WireMock
    * 模擬 HTTP servers — 在這測試裡就是模擬 Order Service
    * Spring Cloud Contract 會配置好 WireMock
* REST API 的合約範例
    ![](https://i.imgur.com/RvOHYNI.png)
    * 合約指定的 request 
        * the HTTP method
        * the path
        * optional headers
    * 合約指定的 response 
        * the HTTP status code
        * optional headers
        * expected body 
* ORDER SERVICE 的 CDC 整合測試 
    ![](https://i.imgur.com/qJDwEYm.png)
    ![](https://i.imgur.com/8ielEei.png)
    * HttpBase class
        * code-generated by Spring Cloud Contract
        * 設定好模擬的依賴項等等 做到可以產出合約指定的回應
        * 當 Order Service 收到 GET/orders/{orderId} 運行OrderRepository’s findById() 後驗證回應是否正確
* CONSUMER-SIDE 整合測試 API GATEWAY’S ORDERSERVICEPROXY
    ![](https://i.imgur.com/g1jfVTN.png)
    ![](https://i.imgur.com/YhHpZHT.png)
    * API Gateway’s OrderServiceProxy 呼叫 GET /orders/{orderId} endpoint
    * OrderServiceProxyIntegrationTest test class
        * 驗證模擬是否符合合約
    * 可以呼叫 OrderServiceProxy 模擬真正的呼叫
        * shouldVerifyExistingCustomer()模擬成功呼叫
        * shouldFailToFindMissingOrder()模擬呼叫失敗
### 10.1.3 針對 publish/subscribe-style interactions 的整合測試
* 會有服務發佈 domain events 也會有服務關注
    * 像是Order相關的events是在有創建或是更新訂單的時候會發佈，而Order History Service會訂閱
    * 需要驗證互動是不是正確的
    ![](https://i.imgur.com/TJjlODS.png)
    * consumer-side 測試驗證OrderHistoryEventHandlers是否使用合約中的範例events
    * provider-side 測試驗證OrderDomainEventPublisher是否發布了確認合同的事件
* 發佈訂單事件的合約 
    ![](https://i.imgur.com/kYp6BlK.png)
    * label — consumer 使用它來觸發Spring Contact發布事件
    * triggeredBy — 測試的時候會被呼叫的方法
* provider-side test for Order Servic
    ![](https://i.imgur.com/6GD7qkr.png)
    ![](https://i.imgur.com/SgdOkRq.png)
    * MessagingBase
        * 負責配置 OrderDomainEventPublisher 讓他使用 in-memory messaging stubs
        * orderCreated()
            * 被合約呼叫來觸發以及驗證事件是否符合合約
* consumer-side
    ![](https://i.imgur.com/AoNg0Rq.png)
    ![](https://i.imgur.com/RC6lktP.png)
    * 先配置好會需要用到的模擬
    * shouldHandleOrderCreatedEvent()
        * 會先呼叫Spring Cloud發佈事件
            * stubFinder.trigger("orderCreatedEvent");
        * 收到事件後再去驗證接下來的動作是否正確
            * 像是呼叫OrderHistoryDao更新CQRS view
### 10.1.4 針對非同步request/response style interactions的整合測試
* Order Service 使用 sagas會將command messages 發送到其他服務，然後等待處理回來的訊息
    * 這些回覆需要遵守 command message channel 的名稱跟回覆command 的架構 
    * 跟測試REST interactions蠻像的，HTTP變成但input message跟output message
* ![](https://i.imgur.com/eVkG8vt.png)
    * KitchenServiceProxyTest 測試驗證 
    * provider-side 測試驗證 KitchenServiceCommandHandler 處理commands 並回覆
    * consumer-side 測試驗證 KitchenServiceProxy 發送合約指定的 commands 處理合約的回覆 
* 非同步 REQUEST/RESPONSE 合約
    ![](https://i.imgur.com/U0myc77.png)
    * 合約是從provider的觀點去寫的 在這就是kitchenService 
    * 兩種 messages 都有指定 
        * message channel
        * message body
        * message headers
    * input message 的 CreateTicket command 是發送到 kitchenService channel
    * output message 是回覆到 CreateOrderSaga’s reply channel
* CONSUMER-SIDE 的非同步 REQUEST/RESPONSE INTERACTION 合約整合測試
    ![](https://i.imgur.com/zWLaaar.png)
    * Spring Cloud Contract會配置好KitchenServiceProxy模擬KitchenService 
    * shouldSuccessfullyCreateTicket()
        * 發送CreateTicket 並驗證
* PROVIDER-SIDE 的非同步 REQUEST/RESPONSE INTERACTION 合約整合測試
    ![](https://i.imgur.com/9wlz4Aa.png)
    ![](https://i.imgur.com/lxfw7DS.png)
    *  AbstractKitchenServiceConsumerContractTest class
        *  KitchenServiceCommandHandler使用模擬的KitchenService
## 10.2 寫部件試驗測試
* component tests 
    * 驗證一整個服務的流程 像是Order Service
    * 可以獨立驗證服務的行為
* Domain-Specific Languages (DSL) 
    * Gherkin 來寫 acceptance tests（驗收測試）
![](https://i.imgur.com/EogjgDn.png)
### 10.2.1 定義驗收測試
* 驗收測試是針對軟體部件的面向業務的測試（更實際）
* 它們從客戶的觀點描述所需要的行為
* 這些測試來自客戶故事或例子
    * 像是Order Service的故事 - Place Order 故事
    * 每個 scenario 定義一個驗收測試
    * givens - 測試的設置階段
    * when - 執行的句子
    * then 跟 and - 驗證的句子
> ![](https://i.imgur.com/CsIS0yK.png)
* 上面描述做的事情
    * Creates an Order by invoking the POST /orders endpoint 
    * Verifies the state of the Order by invoking the GET /orders/{orderId} endpoint 
    * Verifies that the Order Service published an OrderAuthorized  event by subscribing to the appropriate message channel 
> 可以用來轉換成Java的程式碼 但上面的描述不夠好 用Gherkin更好
### 10.2.2 使用Gherkin寫驗收測試
* 寫驗收測試可能
    * 高階的描述跟低階的Java實作有脫節
    * 模稜兩可轉換不成Java
* Gherkin是用於寫可執行specifications的DSL
    *  English-like scenarios
    *  用Cucumber執行specifications
        * Gherkin裡自動測試的框架
    * feature - 很多的情境描數
    * scenario - given-when-then架構
    * givens - 前提條件
    * when - 發生的動作或事件
    * then/and - 預期的結果
* Order Service的範例用到下訂單、取消訂單、修改訂單
    * Name - 下訂單
    * pecification brief - 用戶故事的簡介
    * Scenarios - 訂單被授權 跟 因為信用卡過期訂單被拒絕
    ![](https://i.imgur.com/A6AC7Hj.png)
* EXECUTING GHERKIN SPECIFICATIONS USING CUCUMBER 
    * stepDefinition class
        * 寫著你的每個步驟
![](https://i.imgur.com/NTA4uU7.png)
* @Given - The setup phase 
* @When - The execute phase 
* @Then ,@And - The verification phase 
### 10.2.3 設計部件測試
* in-process component tests
    * in-memory stubs and mocks for its dependencie
* out-process component tests
    * production-ready format and run it as a separate process
### 10.2.4 為FTGO的Order Service寫部件測試
![](https://i.imgur.com/FUx3nxw.png)
* The OrderServiceComponentTestStepDefinitions class
    * usingCreditCard()
    ![](https://i.imgur.com/PLorMDD.png)
    ![](https://i.imgur.com/QtQf9Fr.png)
    * placeOrder()
    ![](https://i.imgur.com/fkQ0NIO.png)
    * theOrderShouldBe()
    ![](https://i.imgur.com/TmuRzWP.png)
    * verifyEventPublished()
    ![](https://i.imgur.com/6AFVTxQ.png)
    ![](https://i.imgur.com/AUyhvci.png)
* RUNNING THE COMPONENT TESTS 
    * 把程式碼放到src/component-test/java
    * 執行./gradlew componentTest
    * 從ftgo-order-service/build.gradle Gradle的配置
* 用Docker運行Order Servic
## 10.3 端到端測試
* slow, brittle, and time consuming to develop
* 因為要用到的服務粉多一個壞了就GG 能少測就少測
    ![](https://i.imgur.com/kPJpLDr.png)
### 10.3.1 設計端到端測試
* user journey test
    * 一次寫好會用到的功能一次測
        * 像是創建訂單 修改訂單 取消訂單
### 10.3.2 寫端到端測試
* 也是用Gherkin跟Cucumber
![](https://i.imgur.com/F8wrBtA.png)
### 10.3.3 運行端到端測試
* Gradle Docker Compose plugin 提供方便的方法，可以一次運行所有服務 
* end-to-end tests跟component tests很像也是用Gherkin跟Cucumber
* 但會著container越來越多，測試越來越多，時間會越來越長
    * 還是做好金字塔下面的測試
## Summary 
* 用consumer-driven contract來做測試
* component tests 透過API來測試服務
* user journey tests 縮小 end-to-end tests