這是一個40多歲還在編碼的老程序員對單元測試的理解和實踐。里面沒有廢話,希望每句話能說到你心坎里。
原則:只測自己
自己的含義:方法邊界內的主體邏輯。一切下游方法、框架依賴、外部IO等都不是自己。如spring、 外部數據庫都視為外部邏輯。
這一原則的動機
便於定位
每個方法有自己獨立的單元測試,這有利於IDE在單元測試與邏輯代碼間跳躍,便於定位,並降低代碼結構調整的影響范圍。
不重復,降低復雜度
因每個方法有自己的單元測試,所以當前測試不要涵蓋下游方法的功能測試。以避免邏輯改動造成不必要的影響范圍擴大。
實踐與建議
要想單元測試更易於維護,良好的設計是前提。
分層開發
開發也是可以分層的,先進行框架開發后進行具體開發。如先定義好所有的接口和要傳遞的數據,並組織好控制層,然后測試和具體開發便可同時展開。這樣便可提前發現結構性問題,提前對設計進行驗證,減少后期結構性調整對單元測試的影響。
單一職責
盡量應用單一職責設計模式,使邏輯模塊化,並使用老板原則將這些模塊串起來。這對於類的內部實現尤其重要,因為這會影響單元測試的覆蓋能力。
只有這樣每個方法的邏輯才能簡單,對應的單元測試自然便於維護。作為老板的控制類或方法不必苛求單元測試,可酌情選擇是否單元測試,因為控制是少數派,且實質邏輯已經被分攤到模塊中。
邏輯與外部IO分離
這是單一職責的延申,數據的加載、處理與輸出是可以作為獨立的三個職責的。其中數據的加載和輸出往往與外部環境依賴有關,本身的測試意義不大,即便測試也不能到處運行,且測試運行效率低下。
所以我們需要將IO邏輯從處理邏輯中剝離,並放到外層的控制邏輯中去並用mock來解決。這樣沒有外部依賴的處理邏輯
——核心,將得到簡化,從而單元測試得到簡化。
如果中間實在需要外部IO可考慮在IO處進行邏輯拆分,這樣拆分后的子邏輯中就沒有了IO依賴,從而得到簡化。
到處運行
有時候單元測試是脫離不了環境的,如我們想驗證一下 SQL 的正確性。此時建議在單元測試上應用 @Disabled
注解,以便在 mvn test 中忽略這些測試,從而保證測試可以到處運行。
@Autowired
請盡量避免對框架的依賴,如 spring 的 @Autowired 注入機制,這會巨幅增加單元測試的構建難度,巨幅增加單元測試的耗時,因為這會對 mock 對象的注入造成困難。
建議,使用構造注入代替屬性注入,這樣就可以擺脫對 spring 的依賴。