理解單元測試(Unit Testing)
本文的目的是以最精煉的語言,正解什么是單元測試,為什么要單元測試,和如何進行單元測試。
什么是單元測試(Unit Testing)?
測試(Testing)這個詞很容易理解,那么什么是單元(Unit)呢?一個單元指的是應用程序中可測試的最小的一組源代碼。一組源代碼可測試,一般要求其有明確的輸入和輸出。因此,一般來講,源代碼中包含明確的輸入和輸出的每一個方法被認為是一個可測試的單元。注意,這里指的輸出,并不局限于方法的返回值或對輸入參數的改變,而包括了方法的執行過程中,改變的任何數據。
為什么要做單元測試?
單元測試的目的,是將應用程序的所有源代碼,隔離成最小的可測試的單元,保證每個單元的正確性。理想情況下,如果每個單元都能保證正確,就能保證應用程序整體相當程度的正確性。
另一方面,單元測試也是一種特殊類型的文檔,相對于書面的文檔,測試腳本本身往往就是對被測試代碼的實際的使用代碼,對于幫助開發人員理解被測試單元的使用是相當有幫助的。
如何進行單元測試?
隔離
要進行單元測試,首先就是要將被測試的單元,與所有外部依賴進行隔離。一般來講,進行隔離主要有兩種可選的技術,一種叫Stub,一種叫Mock。對于兩個的區別和取舍,Martin Fowler有一個很經典的文章:Mocks Aren't Stubs。簡單來說,如果要Stub或Mock一個方法,Stub指的是,為了測試,手動寫一個相同簽名的方法,根據我的測試需要,對給定的輸入參數,返回一定的輸出;而Mock則借助一定的框架組件,自動生成一個方法的實現,對于給定的輸入,返回一定的輸出。當然,所謂的Stub或Mock,并不單指對方法,尤其是在面向對象編程中,也可以是對屬性,對對象或者對接口,等等。
為了方便實現隔離,對軟件設計和開發的一個潛在的激勵是,軟件模塊的設計人員和開發人員,不得不時時思考,在當前語言支持的各種特性的條件下,如何使得所寫的代碼,能夠被方便的被隔離。雖然這看起來很像是一種限制,但是和軟件設計的SOLID原則,其實是不謀而合的,因此,也就未嘗不是一個優點了。
測試
解決了隔離的問題,我們就可以關注對一個單元的測試本身了。首先要提的一點是,所謂測試一個單元,一般往往是寫一段測試腳本,給定輸入,驗證輸出。但是,其實并非所有的單元測試都可以完全由計算機方便地自動完成的。尤其是如圖形UI這樣的比較抽象元素的判斷。因此,從相對廣義的角度來說,凡是能夠用于驗證一個被測試的單元正確性的所有方法進行的測試,都可以被認為是有效的測試。
自動化
解決了隔離和測試本身的問題,下一個需要討論的就是測試的效率的問題。盡管上面提到了,所謂單元測試,不完全都可以由計算機方便地自動完成,但是,對于那些可以由計算機自動完成的單元測試,如何提高寫測試代碼和運行測試的效率呢?答案是,陸續出現了很多的測試框架和測試工具。這些測試框架或工具,一般都會提供提高創建測試腳本效率的工具,并且,提供自動化執行測試和查看執行結果的功能,往往還可以很方便的和代碼的自動化編譯和部署進行集成,使測試自動化。
Guidelines
這里整理了一些如何寫好測試代碼的Guidelines:
- 有測試總比沒測試好
- 測試盡可能的小而快
- 使測試自動化
- 進行代碼覆蓋檢測
- 在寫任何新的實現代碼或測試代碼之前,先修復所有運行失敗的測試
- 再簡單的代碼也需要測試
- 特別注意輸入參數的邊界案例
- 不要只測試正常流程,還要測試可選和例外流程
- 對于每一個報告的Bug,寫一個對應的測試用例,方便回歸測試
- 時刻注意,所有測試都通過,不代表程序就真的100%沒問題,測試永遠只是輔助