總覽 第2章 首個單元測試 第3章 使用JUnit編寫測試 4. 測試什么? 5.CORRECT(正確的)邊界條件 6.使用Mock對象 7. 好的測試所具有的品質(A-TRIP) 8. 在項目中進行測試 9. 設計話題 總覽
這是本相對簡單的書,書中采用的JUnit的版本也是舊的,但是在新的JUnit4下稍做修改依然可以運行。重要的是通過這本書了解JUnit在Java的單元測試中是如何使用的。
第2章 首個單元測試
計劃你的測試:測試不是無中生有的,也不是意想天開的。是根據需要一點點添加的,幫助自己盡早地發現思考上的誤區。參看這章給出的例子,原來理所當然正確的,結果不一定是正確的。
第3章 使用JUnit編寫測試
3.1 構建單元測試
測試代碼必須要做的幾件事情:
后面還介紹了(3.5 JUnit的自定義斷言)
3.3 JUnit框架
這章是基于JUnit3.x寫的,建議了解就可以了,因為JUnit4的變化較大,使用也更方便直觀,因此直接參考JUnit4的幫助。
框架運行順序 | 對應于標簽 |
setUpBeforeClass() | @BeforeClass |
| |
setUp() | @Beofre |
testMethod1() | |
tearDown() | @After |
| |
setUp() | @Before |
testMethod2() | |
tearDown() | @After |
| |
tearDownAfterClass() | @AfterClass |
4. 測試什么?
6個需要測試的地方(Right-BICEP):
- Right:結果是否正確(Right);
- B:邊界(Boundary)條件是否正確(CORRECT);參考第5章
- I:能否檢查反向(Inverse)關系;
- C:進行交叉檢查(Cross-Check)的其他手段;
- E:強制錯誤(Error)條件發生;使用Mock對象實現,參考第6章
- P:滿足性能(Performance)的要求。
測試內容較多時,可以使用測試數據文件進行準備。但是使用文件后就沒有測試代碼看起來那么直觀了,因此除非測試內容非常復雜,否則沒有必要采用這樣的方式。并且如果測試文件出現錯誤(作者書中就出現了數據錯誤),還會導致測試不通過,增加了維護的成本。
5.CORRECT(正確的)邊界條件
- 一致性(Conformance):值是否符合預期的格式;
- 有序性(Ordering):一組值是否符合對排序的要求(有序性、無序性);
- 區間性(Range):值是否在合理取值范圍內(在最小值與最大值之間);
- 引用(Reference)-耦合性:代碼是否引用了不受代碼本身直接控制的外部因素;
- 存在性(Existence):值是否存在(例如:非NULL,非零,包含于某個集合等等)
- 基數性(Cardinality):是否恰好有足夠的值;(也稱為集合的勢,即集合里面包含的元素個數)
- 時間性(Time)-絕對時間和相對時間:所有的事情是否按照順序發生?是否在正確的時間發生?是否及時發生?
6.使用Mock對象
Mock對象解決的問題:
- 真實對象具有不可確定的行為(如:股票行情);
- 真實對象很難被創建;
- 真實對象的某些行為很難被觸發(如:網絡錯誤);
- 真實對象令程序的運行速度很慢;
- 真實對象有用戶界面或者就是用戶界面;
- 真實對象需要被詢問它是如何被調用的(如:驗證某個回調函數是否被調用);
- 真實對象實際上不存在(如:其他開發小組的接口、或者某個沒有的硬件產品)。
Mock對象解決的步驟:
- 使用一個接口來描述這個對象;
- 為產品代碼實現這個接口;
- 以測試為目的,在Mock對象中實現這個接口。
注:這里的Mock不是網上已經形成框架的Mock工具,是Mock的實現原理。作者推薦的Mock工具是EasyMock。其他的Mock工具可以參考《[使用Mock進行單元測試]》(https://blog.csdn.net/u011393781/article/details/52669772)
7. 好的測試所具有的品質(A-TRIP)
- 自動化(Automatic):自動化地調用測試和檢查結果;常用的持續集成工具
- 徹底的(Thorough):測試了所有需求關注的情況;常用的代碼覆蓋工具
- 可重復(Repeatable):每個測試應該獨立于其他所有的測試,還必須獨立于環境,從而可以重復地執行,并且產生相同的結果。
- 獨立的(Independent):確保一個函數只針對一樣測試,并且這個測試不依賴于其他測試。
- 專業的(Professional):測試代碼應該與產品代碼的編碼風格和編寫質量相同
如何確保測試代碼是正確的呢?
- 對產品代碼中的Bug進行修改的時候也改進測試代碼;(因為這個Bug是測試代碼沒有發現的)
- 在產品代碼中引入Bug來驗證測試代碼的正確性。(確保可能會發生的錯誤被測試代碼捕捉到了)
8. 在項目中進行測試
- 把測試代碼與產品代碼放在一個目錄下;
- 與別人共享代碼的時候,需要確保你的代碼可以通過所有測試;
- 測試的時間點:
- 編寫新的函數;
- 修正Bug;
- 每次成功編譯之后;
- 每次對版本控制的提交;
- 持續不斷地由專門的機器來運行完整的構建和測試。
- 測試別人的項目代碼:其實就是維護別人的項目絕對是個大問題,同時也是個必須面對的問題。需要理性的態度(不批評別人的代碼)、冷靜的手段(不隨便修改別人的代碼)、持久的耐心(先從測試代碼開始,慢慢重構項目代碼,使之重新回到健康狀態)、真正的智慧(知道什么樣的項目應該達到什么樣的目標,不執著于重構成一個完美的狀態,也不簡單放棄隨之自生自滅。)
- 測試與評審:三個臭皮匠頂個諸葛亮,放下自我的執著,接納各種不同的意見,才能做出令自己滿意的項目。
9. 設計話題
- 面向測試的設計:不方便測試的設計不是好的設計;說明設計過于僵化或者臃腫,需要簡化或者修改使之更利用未來的擴展和維護。
- 面向測試的重構:不方便測試的代碼不是好的代碼;說明業務混雜在一起,無法實現一個函數只針對一樣測試,需要修改設計使業務分離。
- 測試類的不變性:就是對類的斷言必須為真。
- 有序性。例如:sorted list類的不變性就是無論發生什么,結果都應該是有序的。
- 結構化。例如:訂單系統中每個條目必須屬于一個訂單,一個訂單擁有一個或多個條目。
- 數學不變性。例如:銀行賬號的的借貸必須平衡。
- 數據一致性。例如:商品總數=庫存數+銷售數。
- 測試驅動的設計。使你作為產品代碼的用戶在編碼,而不是產品開發者在編碼,開發結果更能反應用戶的需求。
- 測試無效的參數。當你作為產品代碼的用戶時,你才能真正確定哪些責任應該你來承擔,而哪些是不需要的。例如:無效的參數應該由哪個函數來承擔檢查責任呢?