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






