《單元測試的藝術(shù)(第2版)》是經(jīng)典的單元測試學(xué)習(xí)指南,分四部分全面介紹了單元測試技術(shù)。第一部分闡述單元測試基本概念,包括如何使用測試框架。第二部分討論破除依賴的高級技術(shù):模擬對象、存根和隔離框架,包括重構(gòu)代碼以使用這些技術(shù)的模式。第三部分介紹測試代碼的組織方式、運(yùn)行測試和重構(gòu)測試結(jié)構(gòu)的模式,以及編寫測試的最佳實(shí)踐。第四部分介紹如何在組織內(nèi)實(shí)施變革和修改現(xiàn)有代碼。
《單元測試的藝術(shù)(第2版)》適合所有語言的測試和開發(fā)人員,特別是測試主管和項目經(jīng)理。
基礎(chǔ)概念+代碼+具體分析,手把手教你學(xué)測試
解密神秘魔法:隔離框架
測試“不可測試”的代碼
我工作過的最失敗的一個項目就使用了單元測試。或者說,我是這么認(rèn)為的。那時我?guī)ьI(lǐng)著一隊程序員開發(fā)一個記賬應(yīng)用,采取的是徹底的測試驅(qū)動開發(fā)方式:編寫測試,然后編寫代碼;看到測試失敗,使測試通過,重構(gòu)代碼;然后再開始新一輪過程。
項目前期的幾個月非常好,所有的事情都很順利,我們有測試可以證明代碼工作正常。但是隨著時間的推移,需求發(fā)生了變化。我們被迫修改代碼以適應(yīng)新的需求,但是這樣一來,測試失敗了,需要修復(fù)。產(chǎn)品代碼還是可以正常工作的,但是我們編寫的測試過于脆弱,代碼中任何微小的改變都會導(dǎo)致測試失敗,哪怕代碼工作正常。修改類或方法中的代碼成了一項令人生畏的任務(wù),因為同時還需要修復(fù)所有相關(guān)的單元測試。
更糟糕的是,因為有些人離開了這個項目,沒有人知道他們測試的是什么,也不知道如何維護(hù)他們的測試,這些測試就無法使用了。我們給單元測試方法起的名字不夠清楚,還有的測試互相依賴。最后的結(jié)果是,項目才開始不到6個月,我們就扔掉了大部分的測試。
這個項目是個悲劇,我們讓自己編寫的測試造成的損失比帶來的收益多。從長遠(yuǎn)來看,維護(hù)和理解這些測試花費(fèi)的時間,超過了它們能為我們節(jié)省的時間,因此我們停止使用它們了。我此后又參加過別的項目,這些項目中的單元測試做得好一些,我們使用這些測試獲得了很大的成功,節(jié)省了大量的調(diào)試和集成時間。從那第一個失敗的項目之后,我一直在整理單元測試的最佳實(shí)踐,并在之后的項目中應(yīng)用。在每個工作過的項目中,我總能找到更多的最佳實(shí)踐。
理解如何編寫單元測試,以及如何使它們可維護(hù)、可讀和可靠,是這本書的內(nèi)容,不管你使用的是何種語言或集成開發(fā)環(huán)境(IDE)。這本書涵蓋了編寫單元測試的基本知識,然后講解交互測試基礎(chǔ),介紹了在真實(shí)世界中編寫、管理和維護(hù)單元測試的最佳實(shí)踐。
Roy Osherove,世界著名單元測試專家,常年為世界各地的開發(fā)團(tuán)隊提供咨詢和培訓(xùn)服務(wù),并在各種大會上發(fā)表演講,內(nèi)容包括單元測試及測試驅(qū)動開發(fā)的藝術(shù)、團(tuán)隊領(lǐng)導(dǎo)力和敏捷開發(fā)實(shí)踐。其個人技術(shù)博客osherove.com平均月獨(dú)立訪問量約50 000,提供了各種技術(shù)視頻及其他培訓(xùn)信息,另著有Notes to a Software Team Leader: Growing Self Organizing Teams。
第一部分 入門
第1章 單元測試基礎(chǔ) 2
1.1 逐步定義單元測試 2
1.1.1 編寫優(yōu)秀單元測試的重要性 4
1.1.2 我們都寫過(某種)單元測試 4
1.2 優(yōu)秀單元測試的特性 5
1.3 集成測試 5
1.4 什么是優(yōu)秀的單元測試 9
1.5 一個簡單的單元測試范例 9
1.6 測試驅(qū)動開發(fā) 12
1.7 成功進(jìn)行TDD的三種核心技能 15
1.8 小結(jié) 15
第2章 第一個單元測試 17
2.1 單元測試框架 18
2.1.1 單元測試框架提供什么 18
2.1.2 xUnit框架 20
2.2 LogAn項目介紹 20
2.3 NUnit初步 20
2.3.1 安裝NUnit 21
2.3.2 加載解決方案 22
2.3.3 在代碼中使用NUnit屬性 24
2.4 編寫第一個測試 25
2.4.1 Assert類 25
2.4.2 用NUnit運(yùn)行第一個測試 26
2.4.3 添加正檢驗 27
2.4.4 從紅到綠:測試成功 28
2.4.5 測試代碼格式 28
2.5 使用參數(shù)重構(gòu)測試 28
2.6 更多NUnit屬性 30
2.6.1 setup和teardown 30
2.6.2 檢驗預(yù)期的異!33
2.6.3 忽略測試 35
2.6.4 NUnit的方法語法 36
2.6.5 設(shè)置測試類別 37
2.7 測試系統(tǒng)狀態(tài)的改變而非返回值 37
2.8 小結(jié) 41
第二部分 核心技術(shù)
第3章 使用存根破除依賴 44
3.1 存根簡介 44
3.2 發(fā)現(xiàn)LogAn中對文件系統(tǒng)的依賴 45
3.3 如何使測試LogAnalyzer變得容易 46
3.4 重構(gòu)代碼設(shè)計以提高可測試性 48
3.4.1 抽取接口使底層實(shí)現(xiàn)可替換 49
3.4.2 依賴注入:在被測試單元中注入一個偽實(shí)現(xiàn) 51
3.4.3 在構(gòu)造函數(shù)層注入一個偽對象(構(gòu)造函數(shù)注入) 51
3.4.4 用偽對象模擬異常 55
3.4.5 用屬性get或set注入偽對象 56
3.4.6 在方法調(diào)用前注入偽對象 57
3.5 重構(gòu)技術(shù)變種 63
3.6 克服封裝問題 65
3.6.1 使用internal和[InternalsVisibleTo] 65
3.6.2 使用[Conditional]屬性 66
3.6.3 使用#if和#endif進(jìn)行條件編譯 66
3.7 小結(jié) 67
第4章 使用模擬對象進(jìn)行交互測試 68
4.1 基于值的測試、基于狀態(tài)的測試和交互測試 68
4.2 模擬對象和存根的區(qū)別 70
4.3 手工模擬對象的簡單示例 71
4.4 同時使用模擬對象和存根 73
4.5 每個測試一個模擬對象 78
4.6 偽對象鏈:用存根生成模擬對象或其他存根 78
4.7 手工模擬對象和存根的問題 79
4.8 小結(jié) 80
第5章 隔離(模擬)框架 81
5.1 為什么要使用隔離框架 81
5.2 動態(tài)生成偽對象 83
5.2.1 在測試中使用NSubstitute 83
5.2.2 用動態(tài)偽對象替換手工偽對象 84
5.3 模擬值 86
5.4 測試事件相關(guān)的活動 92
5.4.1 測試事件監(jiān)聽者 92
5.4.2 測試事件是否觸發(fā) 93
5.5 現(xiàn)有的.NET隔離框架 94
5.6 隔離框架的優(yōu)缺點(diǎn) 95
5.6.1 使用隔離框架時應(yīng)避開的陷阱 96
5.6.2 測試代碼不可讀 96
5.6.3 驗證錯誤的事情 96
5.6.4 一個測試多個模擬對象 96
5.6.5 過度指定測試 97
5.7 小結(jié) 97
第6章 深入了解隔離框架 99
6.1 受限框架及不受限框架 99
6.1.1 受限框架 99
6.1.2 不受限框架 100
6.1.3 基于探查器的不受限框架如何工作 101
6.2 優(yōu)秀隔離框架的價值 103
6.3 支持適應(yīng)未來和可用性的功能 103
6.3.1 遞歸偽對象 104
6.3.2 默認(rèn)忽略參數(shù) 104
6.3.3 泛偽造 105
6.3.4 偽對象的非嚴(yán)格行為 105
6.3.5 非嚴(yán)格模擬對象 106
6.4 隔離框架設(shè)計反模式 106
6.4.1 概念混淆 106
6.4.2 錄制和重放 107
6.4.3 粘性行為 109
6.4.4 復(fù)雜語法 109
6.5 小結(jié) 109
第三部分 測試代碼
第7章 測試層次和組織 112
7.1 運(yùn)行自動化測試的自動化構(gòu)建 112
7.1.1 構(gòu)建腳本結(jié)構(gòu) 113
7.1.2 觸發(fā)構(gòu)建和集成 115
7.2 基于速度和類型布局測試 116
7.2.1 分離集成測試和單元測試的人為因素 117
7.2.2 綠色安全區(qū) 117
7.3 確保測試是源代碼管理的一部分 118
7.4 將測試類映射到被測試代碼 118
7.4.1 將測試映射到項目 118
7.4.2 將測試映射到類 118
7.4.3 將測試映射到具體的工作單元入口 119
7.5 注入橫切關(guān)注點(diǎn) 120
7.6 為應(yīng)用程序構(gòu)建測試API 122
7.6.1 使用測試類繼承模式 122
7.6.2 創(chuàng)建測試工具類和方法 133
7.6.3 把你的API介紹給開發(fā)人員 134
7.7 小結(jié) 134
第8章 優(yōu)秀單元測試的支柱 136
8.1 編寫可靠的測試 136
8.1.1 決定何時刪除或修改測試 137
8.1.2 避免測試中的邏輯 140
8.1.3 只測試一個關(guān)注點(diǎn) 142
8.1.4 把單元測試和集成測試分開 143
8.1.5 用代碼審查確保代碼覆蓋率 143
8.2 編寫可維護(hù)的測試 144
8.2.1 測試私有或受保護(hù)的方法 145
8.2.2 去除重復(fù)代碼 146
8.2.3 以可維護(hù)的方式使用setup方法 149
8.2.4 實(shí)施測試隔離 151
8.2.5 避免對不同關(guān)注點(diǎn)多次斷言 156
8.2.6 對象比較 158
8.2.7 避免過度指定 160
8.3 編寫可讀的測試 162
8.3.1 單元測試命名 162
8.3.2 變量命名 163
8.3.3 有意義的斷言 164
8.3.4 斷言和操作分離 165
8.3.5 setup和teardown 165
8.4 小結(jié) 166
第四部分 設(shè)計和流程
第9章 在組織中引入單元測試 168
9.1 逐步成為變革的倡導(dǎo)者 168
9.1.1 準(zhǔn)備好面對質(zhì)疑 169
9.1.2 說服組織內(nèi)成員:支持者和反對者 169
9.1.3 找到可能的切入點(diǎn) 169
9.2 成功之道 171
9.2.1 游擊式實(shí)現(xiàn)(自下而上) 171
9.2.2 說服高層(自上而下) 171
9.2.3 引入外援 172
9.2.4 使進(jìn)度可見 172
9.2.5 設(shè)定具體目標(biāo) 173
9.2.6 應(yīng)對障礙 175
9.3 失敗原因 175
9.3.1 缺少驅(qū)動力 175
9.3.2 缺乏政策支持 175
9.3.3 不好的實(shí)現(xiàn)和第一印象 176
9.3.4 缺少團(tuán)隊支持 176
9.4 影響因素 176
9.5 質(zhì)疑和回答 177
9.5.1 單元測試會給現(xiàn)有流程增加多少時間 178
9.5.2 單元測試是否會搶了QA飯碗 179
9.5.3 證明單元測試確實(shí)有效的方法 179
9.5.4 單元測試有用的證據(jù) 180
9.5.5 QA部門還是能找到缺陷的原因 180
9.5.6 我們有大量沒有測試的代碼:應(yīng)該從哪里開始 181
9.5.7 我們使用多種編程語言:單元測試是否可行 181
9.5.8 軟硬件結(jié)合的開發(fā) 181
9.5.9 確保測試中沒有缺陷的方法 181
9.5.10 我的代碼已經(jīng)調(diào)試通過了,但還需要測試的原因 182
9.5.11 驅(qū)動開發(fā)測試的必要性 182
9.6 小結(jié) 182
第10章 遺留代碼 183
10.1 從哪里開始增加測試 183
10.2 決定選擇策略 185
10.2.1 先易后難策略的優(yōu)缺點(diǎn) 185
10.2.2 先難后易策略的優(yōu)缺點(diǎn) 186
10.3 在重構(gòu)前編寫集成測試 186
10.4 遺留代碼單元測試的重要工具 187
10.4.1 使用不受限的隔離框架輕松隔離依賴項 187
10.4.2 使用JMockit測試Java遺留代碼 189
10.4.3 重構(gòu)Java代碼時使用Vise 190
10.4.4 重構(gòu)前使用驗收測試 191
10.4.5 閱讀Michael Feathers關(guān)于遺留代碼的書 192
10.4.6 使用NDepend調(diào)查產(chǎn)品代碼 192
10.4.7 使用ReSharper瀏覽和重構(gòu)產(chǎn)品代碼 192
10.4.8 使用Simian和TeamCity發(fā)現(xiàn)重復(fù)代碼(和缺陷) 193
10.5 小結(jié) 193
第11章 設(shè)計與可測試性 194
11.1 為什么在設(shè)計時要關(guān)心可測試性 194
11.2 可測試性的設(shè)計目標(biāo) 195
11.2.1 默認(rèn)情況下將方法設(shè)置為虛擬方法 195
11.2.2 使用基于接口的設(shè)計 196
11.2.3 默認(rèn)情況下將類設(shè)置為非密封的 196
11.2.4 避免在包含邏輯的方法內(nèi)初始化具體類 197
11.2.5 避免直接調(diào)用靜態(tài)方法 197
11.2.6 避免在構(gòu)造函數(shù)和靜態(tài)構(gòu)造函數(shù)中包含邏輯代碼 197
11.2.7 把單例邏輯和單例持有者分開 198
11.3 可測試性設(shè)計的利弊 199
11.3.1 工作量 199
11.3.2 復(fù)雜度 200
11.3.3 泄露敏感知識產(chǎn)權(quán) 200
11.3.4 有時無法實(shí)現(xiàn) 200
11.4 可測試性設(shè)計的替代方法 200
11.5 難以測試的設(shè)計示例 202
11.6 小結(jié) 205
11.7 更多資源 206
附錄A 工具和框架 208