在上一篇文章里,我們介紹了測試金字塔以及如何將它應(yīng)用在分布式系統(tǒng)里。
這篇文章將關(guān)注測試金字塔里的單元測試層,并探討如何高效地為分布式系統(tǒng)(如微服務(wù))構(gòu)建單元測試。
定義測試邊界
定義測試邊界是實現(xiàn)高效測試的第一步。測試的目的是為了驗證邊界里“黑盒”的行為是否符合預(yù)期,我們向黑盒輸入數(shù)據(jù),然后驗證輸出的正確性。
在單元測試?yán)?,黑盒指的是函?shù)或者類的方法,目的是單獨測試特定代碼塊的行為。為了更好地理解這個概念,我們以簡單的注冊功能為例:
我們可以看到這個函數(shù)包含了一些輸入和輸出。這個函數(shù)接受基本的用戶注冊信息作為輸入?yún)?shù),并返回新創(chuàng)建的用戶ID。
不過這里也有一些不是很明顯的輸入數(shù)據(jù)。這個函數(shù)調(diào)用了另個外部函數(shù):一個向數(shù)據(jù)庫插入數(shù)據(jù),一個對密碼進行散列和持久化。在某些情況下,數(shù)據(jù)庫可能會返回錯誤。比如,因為用戶名唯一性問題導(dǎo)致數(shù)據(jù)庫插入失敗,又或者需要通過調(diào)用外部的微服務(wù)進行密碼散列,如果網(wǎng)絡(luò)連接出現(xiàn)問題或密碼散列服務(wù)因發(fā)生過載導(dǎo)致服務(wù)超時,那么密碼散列函數(shù)就會返回錯誤。
為了全面測試用戶注冊功能,單元測試所要做的不僅僅是簡單地傳進去不同的輸入?yún)?shù),它還要能夠讓外部依賴項能夠使用這些輸入來驗證函數(shù)的行為是否符合預(yù)期。在測試函數(shù)的錯誤處理邏輯時,這點很重要的。
Stub和Mock
為了制造各種輸入數(shù)據(jù),需要使用stub,也叫作mock。這個可以使用依賴注入或方法攪拌(swizzle)來實現(xiàn)。測試框架在運行被測試的函數(shù)時可以確保對底層依賴項的調(diào)用會被重定向到stub上:
我們可以使用stub來達(dá)到各種目的:
stub可以什么事也不做。這樣可以加快個別單元測試的速度,如果后續(xù)有其他單元測試可用于測試邊界情況的話就可以這樣做。stub可返回任意的值,用于模擬外部函數(shù)的輸出。這在測試罕見的邊界情況時會非常有用,比如有些錯誤場景很少會發(fā)生或者難以重現(xiàn)。stub也可以用于捕捉被測試函數(shù)欲傳給外部函數(shù)的參數(shù),或者把這些參數(shù)記錄下來。這樣就可以驗證被測試函數(shù)需要調(diào)用哪些外部函數(shù)以及需要傳給外部函數(shù)哪些參數(shù)。測試分布式系統(tǒng)需要有一套很好的stub,有了這些stub,單元測試才能夠在沒有外部服務(wù)的情況下運行。下面列出了一些工具,用于創(chuàng)建各種stub。
Node.js/JavaScript
sinon.js (提供了stub和間諜功能)testdouble.js (主要用于面向?qū)ο驛PI的stub生成器)nock (主要用于模擬HTTP請求行為)Python
mockGo
gomockJava
mockitoeasymock單元測試流程
單元測試的目的是為了給開發(fā)人員提供快速驗證他們所寫代碼的行為。因為對外部依賴的調(diào)用使用了stub,所以通常可以在幾秒鐘內(nèi)就可以執(zhí)行數(shù)千個單元測試。所以,開發(fā)人員可以把單元測試加入到他們的開發(fā)工作流當(dāng)中,要么直接集成到他們的IDE里,要么通過終端命令行來運行。開發(fā)人員在編寫代碼的同時頻繁地運行單元測試可以幫助他們及早地發(fā)現(xiàn)代碼中的問題。
一旦開發(fā)人員養(yǎng)成了這樣的習(xí)慣,那么就可以進行測試驅(qū)動開發(fā)了。開發(fā)人員在開發(fā)新特性之前會先準(zhǔn)備好單元測試,在新特性被加進來之前,測試總是失敗。在經(jīng)過不斷的測試和代碼修改之后,一個完整的功能被開發(fā)出來了,最后再運行測試就能通過。
單元測試的作用不應(yīng)局限于代碼開發(fā),它們也應(yīng)該被集成到代碼合并流程里。GitHub支持一些主流持續(xù)集成服務(wù)器的狀態(tài)檢查。一般的流程是這樣的:保護好“master”分支,不允許開發(fā)人員向該分支提交代碼,而是讓他們把代碼提交到其他分支上。在將代碼合并到master分支的時候,GitHub要求先通過狀態(tài)檢查。
Jenkins、CircleCI和TravisCI都提供了狀態(tài)檢查鉤子(hook),它們會從分支上獲取代碼并運行單元測試。如果通過了,就允許合并代碼,否則就不允許。
總結(jié)
單元測試是測試工具箱里的一個非常重要的工具。為了對分布式系統(tǒng)代碼進行全面的單元測試,有必要利用一些支持stub的測試框架,用于模擬各種錯誤場景或外部依賴的各種響應(yīng)。