3.1.2一個測試不是單元測試 如果 -
和數據庫交互
需要跨越網絡
需要訪問文件系統
不能和其他的單元測試同時運行
需要配置文件才能運行
3.1.3測試驅動開發和驗證(verification)無關,和規范(specification)相關
測試驅動開發并不意味著單元測試驅動的開發。實際上,測試驅動開發這個詞非常容易引起誤解。在TDD的大佬們眼中,單元測試只是測試驅動開發的工具,而不是目的。測試驅動開發更關注這個代碼單元應該如何運行(behave)而不是這個代碼單元實現是否正確(verification)。之所以叫測試驅動開發,意義及在于此。最近你可以看到一個新名字的興起-行為驅動開發(Behaviour Driven Developemnt)。BDD更強調一個組件應該如何工作,以及尋找一個簡易的方法來規范行為。
3.1.4為什么需要單元測試?
可以單獨地測試每個單元
可以用于驗證重構后的代碼
可以用來確保不會破壞其他人的代碼
可以用來提高系統設計,比如說,不能單元測試的代碼將不會出現
3.2 對怎么樣的代碼做(不做)單元測試?
參考http://blog.stevensanderson.com/2009/11/04/selective-unit-testing-costs-and-benefits/
【摘要】該文作者在總結3年的TDD實踐經驗的時候,反復認識一個情況:對于某些類型的代碼,單元測試工作得很好,也極大的提高了待測代碼的質量;但對另外一些類型的代碼,單元測試耗費了大量的時間,并沒有起到輔助設計和減少缺陷的作用,同時還導致代碼更加難以維護。
基于這個想法,作者畫了一張圖來表示:
作者認為,
(1)代碼本身很復雜但是對外部的依賴很少(左上),最適合單元測試,因為耗費較少和收益較多。一般來說,某種算法(排序),核心業務規則,數據解析類似的模塊屬于這樣的代碼
(2)帶有很多依賴的瑣碎代碼(Trivial Code with many dependencies,右下):稱為協調者(Coordinators),因為這些代碼主要用于集成多個代碼單元和安排代碼單元之間的交互。這些代碼不適用于單元測試,因為耗費很高,收益卻不大。
(3)代碼很復雜而且外部依賴很多(右上):過于復雜的代碼,需要重構。
(4)外部依賴較少的瑣碎代碼(左下):這些代碼可測可不測,因為重要性不高,復雜度也不高,加單元測試的意義不是很大。
對于作者的這個想法,我比較同意。正如我在2.2.4和2.2.5中提到的,對于一個在線系統(web-based application)來說,可以分為內外兩個圈:內圈由核心領域對象(Core Domain Object)構成,外圈由大量鏈接匯編代碼構成。所謂核心領域對象,這些對象包含核心業務邏輯,數目相對較少,邏輯相對比較復雜,接口(API)相對比較穩定,輸入輸出相對比較清晰,屬于算法類代碼,最合適單元測試,也需要單元測試來提高相應的質量。同時我想強調一下:這些對象一定要精心設計,精挑細選,一定要滿足數量少,接口穩定,輸入輸出清晰這三個要求。數量少意味著需要測的代碼少,相應的單元測試數量也少。接口穩定意味著用戶界面的變化(或需求變化)對單元測試的影響較小。輸入輸出清晰意味著容易驗證邏輯的正確性。相對的,外圈的代碼主要用來鏈接多個領域對象,安排他們之間的交互,轉換成用戶界面需要的數據結構。也就意味著外圈代碼和需求緊密相連,微小的用戶界面變化將會導致相應的代碼代碼,寫單元測試得不償失。
3.3單元測試最佳實踐和測試驅動開發反模式
參考http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
(1)單元測試之間應該完全獨立的
不要寫不必要的斷言,每次只測一個代碼單元,模擬(Mock)外部依賴,避免不必要的前提條件。
(2)單元測試應該運行得很快(比如說少于5 min)
單元測試需要運行得很快,這樣程序員才愿意經常運行單元測試。這就意味著(1)單元測試不要訪問數據庫(2)單元測試不要訪問網絡(3)外部依賴需要被模擬(mock)
(3)給單元測試一個清晰和一致的命名
一個單元測試的名字應該包含3項內容:待測對象_待測案例_期望結果,比如說ProductPurchaseAction_IfStockIsZero_RendersOutOfStockView()。
(4)常見的TDD反模式
撒謊者:所有測試案例都通過了,看上去是有效的。但如果靠近看,你會發現這些案例完全沒有測試預期的內容
過度配置:一個單元測試需要一堆配置然后才能開始測試。有的時候需要幾百行代碼配置環境,設計數十個對象。
巨人:一個單元測試需要測試數千行代碼,并且包含大量的測試用例。
無用者 :有的時候模擬是有效的方便的。但是其他一些時候,過多的模擬對象,Stub對象,假對象,導致單元測試主要在測模擬對象而不是實際的系統。
檢察官:單元測試對待測代碼非常了解,任何對待測代碼的變化將影響單元測試代碼。
慷慨的富有者:多個單元測試共享測試數據,任何一個單元測試改動了一些數據,其他的測試全部失敗了。
本地英雄:單元測試依賴于開發環境某些特定的配置。這意味著:在其他環境上運行單元測試將會失敗。
采集者:單元測試驗證所有的輸出盡管它只對某些數據感興趣。
狗仔隊:單元測試依賴于特定的實現細節,比如說,單元測試捕獲待測代碼中拋出的每一個異常。
欺瞞者:單元測試驗證了一大堆無關的細節,但從不測試核心行為。比如說,待測代碼訪問數據庫并返回數據,單元測試驗證每個返回的數據。
大聲說話的人:單元測試輸出一大堆診斷信息,日志信息,然而沒有清晰的成功/失敗標志。
please refer tohttp://tdd-antipatterns.net/index.php?title=Main_Page
4. 結論(前途在哪里)
現在,我想答案應該比較清楚了。
第一,重新強調單元測試和測試驅動開發的目地:通過寫單元測試來澄清接口(幫助系統設計),確保核心代碼(i.e.Domain Object)的正確性。它不是一個檢測缺陷的有效工具,也不應該只用覆蓋率來衡量單元測試的質量
第二,只對核心代碼(比如說主要業務對象)進行單元測試。待測對象需要有相對有限的API數量,比較穩定的接口定義和清晰的輸入輸出。不要把寶貴的時間花在測試大量的非核心代碼,比如說,鏈接(多個核心對象),協調(多個核心對象),和需求/用戶接口緊密相關的代碼。
第三,不要濫用單元測試,請時刻關注最佳實踐和TDD反模式。
第四,好的單元測試來源于好的設計。設計不好,代碼不會好,單元測試也不會好。
最后,我還想繼續研究行為驅動開發(BDD),看看BDD是否更實用,請繼續關注。
文章來源于領測軟件測試網 http://www.kjueaiud.com/