微服務測試之接口測試和契約測試

日常開發過程中,項目的接口通常由服務提供方約定和提供,微服務模式下接口被多個消費者調用更是常態,那麼提供方接口的變更如何快速、高效、無遺漏的通知給消費者呢?另外,當一個service同時被多個使用者調用,如何保證對service的修改可以讓其它所有使用者造成的影響都能被感知到?這些問題契約測試可以給你答案。另外,微服務模式下,接口測試是非常重要的測試手段,它在實際的項目中幫助驗證微服務之間的協同和交互,大幅降低測試成本和提高測試效率方面提供了很大幫助,可以說接口測試是業務功能測試前置的助推器。因此,這裏對這兩種測試手段進行介紹。

接口測試和契約測試所處的階段

在實際的工作中,結合隨行付的實際情況我們對自動化測試金字塔原理進行了定製,加入契約自動化測試內容,形成如下新版自動化測試金字塔結構。

由圖可知,一個項目的測試過程,從項目推進的維度,首先進行單元測試,其次接口自動化測試、契約測試,最後UI自動化測試和手工測試。

微服務模式下如何開展接口測試

接口測試屬於集成測試範疇,他是單元測試的擴展和延續。它主要的關注點是內部接口功能實現是否完整,比如說內部邏輯是不是正常,異常處理是不是正確。它是單元測試和契約測試的過渡階段,它是項目單個代碼邏輯最終串聯形成有價值業務邏輯的橋樑。因此,其作用舉足輕重。隨行付開展接口測試,採用的思路是規範和方法先行,其次是工具選擇、人員培訓,然後是實施和過程優化,最後常態化持續提效和質量保證的過程。

接口測試規範化要求

接口測試的質量保證和測試過程的流程化需要通過規範和方法進行指導和約束。我們定製瞭如下要求(部分內容):

  1. 需求存在新增接口或者接口變更時,要求進行新增接口測試案例的編寫或存量接口案例的維護;
  2. 需求涉及到的存量接口需要進行迴歸測試;
  3. 接口測試覆蓋率要求達到100%;
  4. 需求測試結束前至少進行一輪接口迴歸測試,且迴歸通過率達到100%

測試流程規範涉及從需求提出、腳本編寫、執行到測試報告的各個過程。

  1. 接口文檔。接口文檔是接口測試案例設計的依據,接口文檔的全面性和準確性決定了接口測試範圍的全面性和接口測試結果的正確性、有效性。隨行付採用swagger進行接口文檔管理。
  2. 接口用例設計。根據接口文檔設計接口測試案例,接口測試案例通過接口測試平臺進行編寫,且需要滿足不重不漏原則。
  3. 接口用例評審。根據項目實際情況,接口測試案例編寫完成後,需組織相關干係人進行案例評審,記錄併發送會議紀要。
  4. 接口用例執行。需求測試結束前接口測試案例至少在測試環境中執行了一次迴歸測試,要求案例執行通過率達到100%
  5. 缺陷管理和測試報告。
  6. 腳本納入迴歸體系,定時迴歸,持續保障接口的質量,以及接口質量的持續和及時反饋。

腳本命名規範和編寫規範如下(部分內容):

  1. 接口命名要求:採用“接口名稱_接口描述”進行命名,用於定義唯一接口。
  2. 方法命名要求:採用“方法名_描述”進行命名,用於定義唯一方法。
  3. 案例命名要求:採用“序號_場景操作_期望結果”進行命名,用於定義唯一案例。
  4. 【強制】每個接口測試案例都必須包含至少一個斷言;
  5. 【強制】對於json格式的報文,接口入參和斷言響應的預期值需要使用嚴格的json格式;
  6. 【強制】swagger腳本導入到接口測試平臺時,需要導入.json文件,且文件內容爲無BOM的UTF-8編碼;
  7. 【強制】數據初始化和斷言的sql必須帶where條件,且能唯一定位到期望的數據;
  8. 【強制】數據庫回退的sql必須帶where條件,且能唯一定位到需要回退的數據;
  9. 【強制】影響公共表(如:T_BAP_CDE_BNK表)或者其他組數據庫表(如:資金組)的sql,在數據初始化、回退、接口影響的數據回退、斷言回退時必須嚴格審查;
  10. 【強制】數據庫斷言sql中的where條件的主鍵組合需要放到前面,用於斷言失敗時快速定位問題;

接口測試用例設計要求

爲了保證接口的質量,需要進行全面的接口測試,因此在涉及接口測試用例時需要依賴方法,因此我們總結了接口測試用例的設計要求,如下圖所示。

接口測試工具

接口測試過程提效、測試過程自動化需要依賴自動化測試工具,武器不好很難打勝仗。經過調研,市面上很多接口自動化測試工具均無法滿足所有的測試要求,因此我們自研了接口自動化測試平臺。自動化測試平臺具有如下能力:

  1. 案例自動生成。http/https接口案例自動化生成和導入。
  2. 測試過程集中可視化管理。通過將自動化測試過程web化實現了自動化測試計劃、自動化測試用例編寫、自動化測試用例執行、自動化測試用例管理和自動化測試報告管理各個過程的可視化。
  3. 模擬性能場景。自動化測試實現了通過接口案例模擬性能測試場景的能力。通過使用平臺中提供的接口案例,進行並行執行模擬性能場景。
  4. 多協議多報文類型支持。支持http/https協議、dubbo協議、socket協議、rabbitMQ協議等協議的自動化測試,並支持對協議的擴展。同時支持xml、json、sop、8583等多種報文類型以及報文類型的擴展。
  5. 測試資產有效積累。
  6. 自動化調度執行和郵件發送。自動化測試執行通過定時對案例進行調度執行,可對指定的構建版本對應的案例進行自動化的分批、定時調度執行並郵件發送測試報告。
  7. 系統質量的可視化反饋。通過對自動化案例的執行結果統計,分析出系統的質量趨勢,做到系統質量的持續化反饋。通過根因分析,統計系統問題的根本原因的比例,更有針對性的解決質量問題。

通過接口測試持續運行1年多的持續運營,隨行付核心業務接口基本實現接口測試用例全覆蓋,且均納入到定期迴歸過程,持續爲接口的質量保駕護航。

微服務模式下如何開展契約測試

契約測試的價值

契約測試分兩種類型,一種是消費者驅動,一種是提供者驅動。其中最常用的,是消費者驅動的契約測試(Consumer-Driven Contract Test,簡稱 CDC)。核心思想是從消費者業務實現的角度出發,由消費者端定義需要的數據格式以及交互細節,生成一份契約文件。然後生產者根據契約文件來實現自己的邏輯,並在持續集成環境中持續驗證該實現結果是否正確。對於基於Restful API的微服務來說,它的契約就是指 API 的請求和響應的規則。
如下圖所示:

  1. 對於請求,包括請求 URL 及參數,請求頭,請求內容等;
  2. 對於響應,包括狀態碼,響應頭,響應內容等。
  3. 對於元數據,指對消費者與提供者間一次協作過程的描述。譬如消費者/提供者的名稱、上下文及場景描述等。

那麼契約測試能給微服務帶來什麼價值呢?文章開頭已經提到了契約測試的一部分價值,即接口變更快速通知,servise修改的快速感知。除此之外,它還帶來下列價值:

  1. 降低服務集成的難度。把服務集成這個過程分解成了更細的單元測試和接口測試,它從消費者的需求爲出發點,把消費者的需求作爲測試用例驅動實現一份契約,然後驗證提供者端的功能。
  2. 開發並行,提高開發效率。契約隔離了消費者和提供者,雙方可以並行開展工作,開發過程中就利用契約進行預集成測試,不用等到聯調再來集成調通接口,一旦成熟,在保證質量的前提下,聯調的成本可以減低到幾乎爲0。
  3. 確保變動的安全性和準確性。只要有變化,契約測試即可第一時間發現,保證安全和對接的準確性。
  4. 作爲Mock server爲消費者提供Mock服務。集成測試爲服務者提供

微服務下如何開展契約測試

隨行付採用在Spring Cloud Contract開展契約測試。其核心流程包括2步:

  1. 對消費者的業務邏輯進行驗證時,先對其期望的響應做模擬提供者(Mock);並將請求(消費者)-響應(基於模擬提供者)的協作過程,記錄爲契約;
  2. 通過契約,對提供者進行回放,保證提供者所提供的內容滿足消費者的期望。

下面用一個簡單的例子說明設計契約測試的方法。這個例子中,一個微服務提供了一個包含三個字段(“IP”、“name”和“password”)的資源,供三個消費者微服務使用。這三個微服務分別使用這個資源中的不同部分。消費者 A 使用其中的 IP 和 name 這兩個字段。因此,測試腳本中將只驗證來自提供者的資源中是否正確包含這兩個字段,而不需要驗證 password 字段。消費者 B 使用 IP 和 password 字段,而不需要驗證 name 字段。消費者 C 則需要確認資源中包含了所有這三個字段。現在,如果提供者需要將 name 分爲姓(first name)和名(last name),那麼就需要去掉原有的 name 字段,加入新的 first name 字段和 last name 字段。這時執行契約測試,就會發現消費者 A 和 C 的測試用例就會失敗。測試用例 B 則不受影響。這意味着消費者 A 和 C 服務的代碼需要修改,以兼容更新之後的提供者。修改之後,還需要對契約內容進行更新。

下面以一個例子介紹如何使用Spring Cloud Contract開展契約測試的。

    1. Spring Cloud Contract契約是用基於Groovy的DSL定義的,下面是一個契約測試的代碼段
 package contracts
  org.springframework.cloud.contract.spec.Contract.make {
  request {
	method 'PUT'
	url '/fraudcheck'
	body([
		   "client.id": $(regex('[0-9]{10}')),
		   loanAmount: 99999
	])
	headers {
		contentType('application/json')
	}
   }
 response {
	status OK()
	body([
		   fraudCheckStatus: "FRAUD",
		   "rejection.reason": "Amount too high"
	])
	headers {
		contentType('application/json')
	}
   }
 } 

    1. 服務方-server (HTTP) / producer (Messaging) 端增加Spring Cloud Contract Verifier的gralde插件,它用於解析契約文件生成測試。生成測試的命令。
./gradlew generateContractTests  

下面是自動生成的測試腳本

@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");
}

這個一個標準的JUnit測試,用RestAssured來啓動Spring的webApplicationContext。

@Before
public void setup() {
	RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
    1. 調用方-在服務方通過命令生成Stub服務的Jar包
./gradlew verifierStubsJar

Spring Cloud Contract Stub Runner在集成測試中通過運行WireMock實例或者消息路由模擬真實的服務。 因此在運行之前,需要將依賴加入到gralde中,當然可以把他加到私服倉庫中。

spring-cloud-starter-contract-stub-runner

對於調用方,Spring Cloud Contract提供了Stub Runner來簡化Stub的使用。現在可以使用@AutoConfigureStubRunner註解.爲了Spring Cloud Contract Stub Runner運行stubs註解中增加了group-id和artifact-id,舉例如下:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"cn.vbill.service:test-client-stubs:1.5.0-SNAPSHOT:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class LoanApplicationServiceTests {
        ......
}

註解AutoConfigureStubRunner,裏面設置了下載Stub Jar包的私庫地址以及包的完整 ID,最後的6565就是指定Stub運行的本地端口。測試的時候訪問Stub端口,就會根據契約返回內容。

由於隨行付微服務是基於spring cloud技術棧,因此採用spring cloud contract進行微服務下的契約測試使得測試過程更流暢,更順利,同時Spring Cloud Contract和SpringBoot以及 Junit的集成更簡單方便。相信隨着spring cloud contract版本的優化,契約測試可以做的更好。

總結

本篇分別從微服務模式下如何開展接口自動化測試,契約測試的價值以及如何開展契約測試角度進行了介紹,微服務模式下,服務間的調用關係複雜,接口測試和契約測試是保證服務提高質量的重要手段,因此要充分利用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章