作者 | Tomas Fernandez
譯者 | 平川
微服務應用程序是一組通過網絡進行通信的分布式程序,有時也會與第三方服務和數據庫交互。微服務是網絡化的,與傳統(tǒng)的單體應用程序相比,它的故障點更多。為此,我們需要一種不同的、涉及面更廣的測試方法。那么,我們該如何測試一個微服務應用程序?測試金字塔還有效嗎?當涉及到第三方服務并可能出現網絡中斷時,我們該如何測試?在這篇博文中,我們將嘗試回答所有這些問題。
本文最初發(fā)布于 semaphore 博客。
微服務應用程序是一組通過網絡進行通信的分布式程序,有時也會與第三方服務和數據庫交互。微服務是網絡化的,與傳統(tǒng)的單體應用程序相比,它的故障點更多。為此,我們需要一種不同的、涉及面更廣的測試方法。
那么,我們該如何測試一個微服務應用程序?測試金字塔還有效嗎?當涉及到第三方服務并可能出現網絡中斷時,我們該如何測試?在這篇博文中,我們將嘗試回答所有這些問題。
微服務測試面臨的挑戰(zhàn)
微服務架構是一種意義深遠的范式變遷,我們必須重新考慮傳統(tǒng)的測試技術。與經典的單體架構相比,微服務在許多方面都有所不同:
-
分布式:微服務部署在多臺服務器上,地理位置可能也不一樣,這會增加延遲,而且會受網絡中斷所影響。測試要依賴網絡,即使代碼沒問題,也可能會失敗,導致 CI/CD 管道中斷,開發(fā)受阻。
-
自治:只要不破壞 API 兼容性,開發(fā)團隊就可以隨時部署他們的微服務。
-
測試區(qū)域擴大:每個微服務都至少會暴露數個 API 端點,因此,測試覆蓋面要更大。
-
多語言:開發(fā)團隊可以選擇最適合其微服務的語言。在一個大型系統(tǒng)中,可能無法找到一個適用于所有組件的測試框架。
-
產品是一個活動目標:由于微服務是由自治團隊單獨部署和構建,所以需要額外的檢查和邊界,以確保它們部署后仍然可以正常運行。
所有這些特點都讓我們不得不考慮新的測試策略。
微服務測試金字塔
測試金字塔是自動化軟件測試的規(guī)劃工具。傳統(tǒng)的金字塔包含 3 種類型的測試:
-
單元測試
-
集成測試
-
端到端測試。
微服務金字塔新增了兩種類型:組件和契約測試
這是微服務測試金字塔的一個版本。其他版本可能順序上會有所不同。有些版本可能將契約測試包含在了集成層。事實上,金字塔更多的是一份指南,而非一成不變的東西。
接下來,我們將對金字塔的每一層做進一步的介紹。
微服務的單元測試
單元測試是粒度最小(數量最多)的測試形式之一。單元由可以單獨測試的類、方法或函數組成。單元測試是開發(fā)實踐中不可分割的一部分,比如測試驅動開發(fā)或行為驅動開發(fā)。
與單體相比,微服務中的單元可能更需要通過網絡調用來完成其功能。這時候,我們可以讓代碼訪問外部服務——就得接受延遲和不確定性,也可以調用測試替身,因此,我們有如下兩種處理微服務依賴的方法:
-
獨立單元測試(Solitary unit tests):如果我們需要測試結果始終是確定的,就應該使用這種方法,通過模擬(mocking)或存根(stubbing)來隔離要測試的代碼和外部依賴。
-
社交單元測試(Sociable unit tests):社交測試允許調用其他服務。在這種模式下,我們把測試的復雜性推到了測試或過渡環(huán)境。社交測試是非確定性的,但如果測試通過,我們對結果會更有信心。
我們可以使用測試替身獨立運行單元測試。我們也可以讓要測試的代碼調用其他微服務,這就是我們正在討論的社交測試。如你所見, 可信度與穩(wěn)定性之間的平衡將貫穿本文始終。模擬可以加快測試速度,降低不確定性,但模擬越多,測試結果的可信度就越低。雖然也有缺點,但社交測試更實用。因此,你要在這兩種類型的測試之間做好權衡取舍。
如果你想實際看下獨立測試和社交測試的例子,可以讀一下 Dylan Watson 在 dev.to 上發(fā)表的那篇不錯的博文。
微服務的契約測試
當兩個服務通過接口耦合時,契約就形成了。契約詳細列出了所有可能的輸入、輸出及其數據結構和副作用。服務的消費者和生產者必須遵守契約描述的規(guī)則才能進行通信。
契約測試可以保證微服務遵守契約。它們不會全面測試服務的行為;它們只確保輸入和輸出具備期望的特性,服務執(zhí)行時間和性能都在可接受的范圍內。
契約測試可以由生產者運行,也可以由消費者運行,還可以兩者都運行,這取決于服務之間的關系。
-
消費者端契約測試由下游團隊編寫并執(zhí)行。測試時,微服務連接到生產者服務的模擬版本,檢查它是否可以消費其 API。
-
生產者端契約測試在上游服務中運行。這類測試會模擬客戶端可以發(fā)起的各種請求,驗證生產者是否符合契約。生產者端測試讓開發(fā)人員可以知道他們什么時候會破壞消費者兼容性。
契約測試可以在上游或下游運行。生產者端測試可以檢查服務變更是否會給依賴它的服務造成破壞。消費者端測試使用上游生產者的模擬版本(并非真正的生產者服務)來運行消費者端組件,驗證消費者是否可以發(fā)起請求,并消費生產者提供的期望響應。我們可以使用類似 wiremock 這樣的工具來再現 HTTP 請求。如果兩端都通過了契約測試,那么生產者和消費者就是兼容的,應該能夠通信。持續(xù)集成時應該總是運行契約測試,以便在部署前發(fā)現不兼容的情況。
你可以在 Pact 的 5 分鐘入門指南里在線試用契約測試。Pact 是一個基于 HTTP 的測試工具,可以編寫和運行基于消費者或是生產者的契約測試。
微服務的集成測試
微服務的集成測試方式與其他架構略有不同,其目標是通過微服務交互來識別接口缺陷。與契約測試總有一端是模擬的不同,集成測試使用真實服務。
集成測試不關注服務的行為或業(yè)務邏輯。集成測試是為了確保微服務可以與其他微服務以及自己的數據庫交互。我們希望通過集成測試來發(fā)現類似 HTTP 頭缺失、請求 / 響應對不匹配這樣的問題。因此,集成測試通常在接口層實現。
使用集成測試來檢查微服務是否可以與其他服務、數據庫和第三方端點通信。建議讀下 Vitaly Baum 關于微服務存根的博文,看下實際的集成代碼測試。
微服務的組件測試
組件是一個較大的系統(tǒng)中可以完成一項職責的一個微服務或一套微服務。
組件測試是驗收測試的一種,使用模擬資源或 mocking 來替換服務,孤立地檢查組件的行為。
組件測試比集成測試更全面,它們既會測試快樂路徑,也會測試不快樂路徑——例如,測試組件如何響應模擬網絡中斷或惡意請求。我們想知道組件是否滿足其消費者的需求,很像我們在驗收測試或端到端測試中所做的那樣。
組件測試執(zhí)行一組微服務的端到端測試。超出組件范圍的服務都是模擬的。
執(zhí)行組件測試的方法有兩種:進程內和進程外。
進程內組件測試
在組件測試的這個子類中,測試執(zhí)行器在和微服務相同的線程或進程內。我們以“離線測試模式”啟動微服務,所有的依賴都是模擬的,這讓我們無需網絡就可以運行測試。
組件測試在和微服務相同的進程內運行。測試在適配器中注入一個模擬服務,以模擬與其他組件的交互。
進程內測試僅適用于組件是單個微服務的情況。乍看之下,組件測試和端到端測試或驗收測試非常類似。唯一的區(qū)別是,組件測試只選取系統(tǒng)的一部分(組件),并將其與其余部分隔離開來。它會對這個組件做全面的測試,以驗證它是否提供了用戶或消費者需要的功能。
組件測試和端到端測試可能看上去類似。但區(qū)別在于,端到端測試在一個類生產環(huán)境中測試整個系統(tǒng)(所有微服務),而組件測試只隔出系統(tǒng)的一部分進行測試。兩種測試都會從用戶(或消費者)的角度來檢查系統(tǒng)行為,模擬用戶可能執(zhí)行的操作。我們可以使用任何語言或框架來編寫組件,但最流行的可能要數 Cucumber 和 Capybara 了。
進程外組件測試
進程外測試適用于任意大小的組件,包括由許多微服務組成的組件。在這類測試中,組件被(原封不動地)部署在一個測試環(huán)境中,所有的外部依賴都是以模擬或存根方式提供。
在這類組件測試中,測試環(huán)境會比較復雜,因為它要模擬系統(tǒng)的其余部分。
要全面了解契約測試的概念,建議研究下 JAVA Spring 契約測試的示例代碼。此外,對于 Java 開發(fā)人員,這篇博文提供了一些在各個層面測試 Java 微服務的代碼樣例。
微服務的端到端測試
到目前為止,我們都是分塊測試系統(tǒng)。單元測試用于分別測試微服務的各個部分,契約測試驗證 API 兼容性,集成測試檢查網絡調用,組件測試用于驗證子系統(tǒng)的行為。只有在自動化測試金字塔的最頂端,我們才是對整個系統(tǒng)進行測試。
端到端(E2E)測試用于確保系統(tǒng)可以滿足用戶需求并實現其業(yè)務目標。E2E 套件應該覆蓋應用程序的所有微服務,并且使用與用戶相同的界面——通常搭配 UI 和 API 測試。
應用程序的運行環(huán)境應盡量接近生產環(huán)境。在理想情況下,測試環(huán)境中應包含應用程序通常需要的所有第三方服務,但有時候,為了降低成本或防止濫用,也可以模擬。
端到端測試是模擬用戶交互的自動化測試。只有外部第三方服務可以是模擬的。
從測試金字塔可以看出,E2E 測試數量最少,這很好,因為它們通常最難運行,也最難維護。只要專注于用戶的操作過程及需求,我們就可以從少數幾個 E2E 測試中獲得很大的價值。
小 結
范式變了,策略也要跟著變。在微服務架構下,測試比以往任何時候都重要,但我們需要調整技術來適應新的開發(fā)模型。系統(tǒng)不再是由單個團隊管理。取而代之,每個微服務的所有者都必須完成好自己的部分,才能保證整個應用程序的正常運行。
有些組織可能會認為,單元、契約和組件測試已經足夠了。其他的則認為需要端到端和集成測試,他們可能會選擇組建一支 QA 團隊,以推動跨團隊的測試。
想要進一步了解微服務嗎?可以閱讀以下幾篇文章:
-
什么是微服務架構?
https://semaphoreci.com/blog/microservice-architecture
-
微服務架構的領域驅動設計原則
https://semaphoreci.com/blog/domain-driven-design-microservices
-
微服務的發(fā)布管理
https://semaphoreci.com/blog/release-management-microservices
-
轉向微服務之前要了解的 12 種單體架構優(yōu)化方法
https://semaphoreci.com/blog/monolith-microservices
感謝閱讀!
https://semaphoreci.com/blog/test-microservices