寫單元測試,我不認為是件容易的事

李學斌發表於2022-01-08

這是一個40多歲還在編碼的老程式設計師對單元測試的理解和實踐。裡面沒有廢話,希望每句話能說到你心坎裡。

原則:只測自己

自己的含義:方法邊界內的主體邏輯。一切下游方法、框架依賴、外部IO等都不是自己。如spring、 外部資料庫都視為外部邏輯。

這一原則的動機

便於定位

每個方法有自己獨立的單元測試,這有利於IDE在單元測試與邏輯程式碼間跳躍,便於定位,並降低程式碼結構調整的影響範圍。

不重複,降低複雜度

因每個方法有自己的單元測試,所以當前測試不要涵蓋下游方法的功能測試。以避免邏輯改動造成不必要的影響範圍擴大。

實踐與建議

要想單元測試更易於維護,良好的設計是前提

分層開發

開發也是可以分層的,先進行框架開發後進行具體開發。如先定義好所有的介面和要傳遞的資料,並組織好控制層,然後測試和具體開發便可同時展開。這樣便可提前發現結構性問題,提前對設計進行驗證,減少後期結構性調整對單元測試的影響。

單一職責

儘量應用單一職責設計模式,使邏輯模組化,並使用老闆原則將這些模組串起來。這對於類的內部實現尤其重要,因為這會影響單元測試的覆蓋能力。

只有這樣每個方法的邏輯才能簡單,對應的單元測試自然便於維護。作為老闆的控制類或方法不必苛求單元測試,可酌情選擇是否單元測試,因為控制是少數派,且實質邏輯已經被分攤到模組中。

邏輯與外部IO分離

這是單一職責的延申,資料的載入、處理與輸出是可以作為獨立的三個職責的。其中資料的載入和輸出往往與外部環境依賴有關,本身的測試意義不大,即便測試也不能到處執行,且測試執行效率低下。

所以我們需要將IO邏輯從處理邏輯中剝離,並放到外層的控制邏輯中去並用mock來解決。這樣沒有外部依賴的處理邏輯——核心,將得到簡化,從而單元測試得到簡化。

如果中間實在需要外部IO可考慮在IO處進行邏輯拆分,這樣拆分後的子邏輯中就沒有了IO依賴,從而得到簡化。

到處執行

有時候單元測試是脫離不了環境的,如我們想驗證一下 SQL 的正確性。此時建議在單元測試上應用 @Disabled 註解,以便在 mvn test 中忽略這些測試,從而保證測試可以到處執行。

@Autowired

請儘量避免對框架的依賴,如 spring 的 @Autowired 注入機制,這會巨幅增加單元測試的構建難度,巨幅增加單元測試的耗時,因為這會對 mock 物件的注入造成困難。

建議,使用構造注入代替屬性注入,這樣就可以擺脫對 spring 的依賴。

相關文章