微服務測試之介面測試和契約測試

heishaovvv發表於2019-04-08

日常開發過程中,專案的介面通常由服務提供方約定和提供,微服務模式下介面被多個消費者呼叫更是常態,那麼提供方介面的變更如何快速、高效、無遺漏的通知給消費者呢?另外,當一個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版本的優化,契約測試可以做的更好。

總結

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

相關文章