在任何組織中,改變人們的習慣更多是心理層面而非技術層面的挑戰。人們不喜歡改變,而改變總是伴隨著大量的 FUD(恐懼、不確定性和懷疑)。本章從作者擔任顧問的實務經驗出發,探討如何成功地將單元測試導入組織,以及需要避開的陷阱。
9.1 成為變革推動者的步驟#
如果你決定成為組織中推動變革的人,首先要接受這個角色。人們會把你視為這件事的負責人,逃避只會讓事情變糟。推動變革需要做到三件事:
- 準備好回答棘手問題 – 人們會問各種尖銳的問題,像是「這要多花多少時間?」、「QA 怎麼辦?」
- 說服組織內部的人 – 在開始推動之前,先爭取內部盟友的支持
- 維持動力 – 有人必須持續推進,確保變革不會因缺乏動力而消亡
9.1.1 準備好回答棘手問題#
做足功課。閱讀本章後面的問答、相關論壇、郵件列表和部落格。與同行交流。如果你能回答自己的棘手問題,就很有可能回答得了別人的。
9.1.2 說服內部人士:支持者與阻礙者#
在組織中逆流而上是一件孤獨的事。如果只有你認為這是個好主意,就不會有人願意去實踐。你需要辨識兩類人:
支持者(Champions):
- 通常是早期採用者,或對你倡導的事物持開放態度的人
- 他們可能已經半信半疑,只是在等待一個開始的契機
- 在其他人之前先接觸他們,徵詢意見,讓他們成為流程的一部分
- 讓他們成為別人遇到問題時可以求助的對象
阻礙者(Blockers):
- 最可能抵抗變革的人,例如認為單元測試會增加開發時間的經理
- 不要去指出他們做錯了什麼,這往往適得其反
- 讓他們參與流程:負責制定測試編碼標準、參與 code review、選擇培訓材料等
- 給他們一個新的責任感,讓他們覺得自己在變革中有一席之地
讓阻礙者成為流程的一部分,而不是對立面。給他們一個角色,他們就不太可能去破壞它。
9.1.3 找到可能的切入點#
大多數成功的導入都採取穩紮穩打的路線:從一個小團隊的試點專案開始,觀察結果,成功後再推廣到其他團隊。以下是一些實用技巧:
| 技巧 | 說明 |
|---|---|
| 選擇小型團隊 | 風險低的小型專案,團隊成員對學習新事物持開放態度。經驗較少的開發者反而更願意改變 |
| 建立子團隊 | 在現有團隊中組成子團隊,針對那些 bug 多、維護困難的「黑洞」模組進行試點 |
| 考慮專案可行性 | 不要一開始就啃硬骨頭,準備至少兩個選項(一個困難的、一個簡單的) |
| 用 code review 作為教學工具 | 在 review 中同時檢視程式碼和測試,這是教導團隊的最佳方式 |
Code review 的幾個重要原則:
- 面對面進行,而非透過遠端軟體
- 最初幾週,review 每一行被 check in 的程式碼
- 加入第三個人旁聽,培養更多能做 review 的人
9.2 成功之道#
組織或團隊可以透過兩種主要方式推動變革:由下而上(bottom up)或由上而下(top down),有時兩者兼用。沒有唯一正確的方式。
9.2.1 游擊式實作(由下而上)#
游擊式實作的核心是:先從一個團隊開始做出成果,再說服其他人。驅動力通常來自厭倦了既定做法的團隊,他們自己研究、自己改變、展示結果。
游擊式實作有兩種形態:
- 先被開發者採納,再被管理層認可 – 可以低調進行,不需要上層知道
- 先由開發者倡導,再被管理層支持 – 與管理層協同進行
開發者日常就會做很多不需要許可的事:除錯、閱讀郵件、撰寫註解、畫流程圖。單元測試也是如此,大多數開發者已經在寫某種形式的測試了,只是需要將時間重新導向到能產生長期效益的測試上。
9.2.2 說服管理層(由上而下)#
由上而下的推動通常由一位經理或開發者發起,透過演講、讀書會或運用職權來推動整個組織朝這個方向前進。
9.2.3 找外部推手#
作者強烈建議引入外部顧問來協助推動變革,因為外部人士有三個優勢:
- 言論自由 – 可以直說「你們的程式碼品質很差」、「你們的測試不可讀」
- 經驗豐富 – 有更多應對內部阻力、回答棘手問題的經驗
- 專職投入 – 不像內部員工還有其他工作要做,顧問可以全職推動這件事
程式碼完整性(Code Integrity) 是作者用來描述團隊開發活動目的的術語,涵蓋程式碼的穩定性、可維護性和回饋機制。包含:自動化建置、持續整合、單元測試與 TDD、程式碼一致性標準、最短時間修復 bug。
9.2.4 讓進度可見#
讓變革的進度和狀態看得見非常重要。在走廊、茶水間等人們聚集的地方張貼白板或海報,展示與目標相關的數據:
- 最近一次 nightly build 中通過和失敗的測試數量
- 哪些團隊已經在使用自動化建置流程
- 測試覆蓋率報告或 Scrum Burndown Chart

Figure 9.1: An example of a test-code-coverage report in TeamCity with NCover
讓進度可見有兩個目標群體:
- 正在經歷變革的團隊 – 獲得成就感與榮譽感,更有動力完成流程,也可以與其他團隊比較進度
- 尚未參與的組織成員 – 引發興趣和好奇心,製造話題和討論,形成一股他們可以自願加入的潮流
9.2.5 設定具體目標#
沒有目標,變革就難以衡量和溝通。以下是一些可以考慮的目標:
| 目標 | 說明 |
|---|---|
| 提高測試覆蓋率(搭配 code review) | 單純追求覆蓋率數字沒有意義,需要搭配 code review 確保測試是有意義的,而非只是為了滿足覆蓋率而寫 |
| 追蹤覆蓋率與 code churn 的比率 | code churn 是建置之間被修改的程式碼行數,修改越少,引入的 bug 越少 |
| 減少 bug 重開率 | 修一個 bug 卻不小心弄壞另一個東西的情況減少,代表系統維護能力提升 |
| 縮短平均 bug 修復時間 | 好的測試和覆蓋率讓你能更快找到和修復問題 |

Figure 9.2: An example test-code-coverage trend report
Steve McConnell 在 Code Complete 中也列出了幾個實用的測試進度指標:
- 每個類別按優先順序的缺陷數量
- 每個常式每測試小時發現的缺陷數量
- 每個測試案例的平均缺陷數量
9.2.6 預期會有障礙#
障礙是不可避免的,有些是技術性的,有些是組織性的。技術問題相對容易解決;組織問題則需要細心和心理學的方法。
- 不要因為一次迭代的暫時失敗就放棄,至少需要幾個月才能開始感到舒適
- 讓管理層承諾至少三個月的嘗試期,即使事情不如計劃
- 在開始之前就取得他們的同意,不要等到壓力最大的第一個月才去說服人
Tim Ottinger 的深刻洞察:「即使你的測試沒有抓到所有的缺陷,它們仍然讓你更容易修復那些沒被抓到的缺陷。」
9.3 失敗之道#
作者列出了幾個導致單元測試導入失敗的常見原因。
9.3.1 缺乏推動力#
在所有失敗案例中,缺乏持續的推動力是最強大的因素。成為變革推動者需要付出代價:花費時間教導他人、幫助他們、進行內部政治戰爭。你必須願意犧牲自己原本的時間,否則變革不會發生。引入外部顧問可以幫助維持推動力。
9.3.2 缺乏政治支持#
有時候反對不是明顯的,而是微妙的。例如老闆說「去做吧,我們給你 10% 的時間」。低於 30% 的時間投入對於開始單元測試來說是不切實際的。這是一種以資源限制來扼殺趨勢的手段。
你需要學會辨識這種隱性反對,一旦發現,就要正視它。
9.3.3 糟糕的初始印象#
如果在沒有足夠知識的情況下就開始實施單元測試,請幫自己一個忙:找有經驗的人協助,遵循最佳實踐。
- 只有幾個月的時間來展現成果,說服上級
- 不要浪費時間重新發明測試方法
- 糟糕的第一印象可能導致試點專案被關閉,失去信譽
9.3.4 缺乏團隊支持#
如果團隊不支持你的努力,幾乎不可能成功。你需要在新流程上的額外工作與日常工作之間取得平衡。
- 逐一與團隊成員談話,爭取個別支持
- 也可以以團隊為單位進行討論,回答他們的問題
- 不要把團隊的支持視為理所當然
9.4 影響因素#
作者引用 Influencer: The Power to Change Anything 一書的核心觀點:每一個你看到的行為,都是為了產生該行為而完美設計的結果。 這意味著除了個人意願和能力之外,還有其他因素在影響人們的行為。
該書提出六個影響因素:
| 因素 | 關鍵問題 |
|---|---|
| 個人能力 | 當事人是否具備所需的技能和知識? |
| 個人動機 | 當事人是否從正確行為中獲得滿足感?是否有自制力去做困難的事? |
| 社會能力 | 你或他人是否在關鍵時刻提供了必要的幫助、資訊和資源? |
| 社會動機 | 周圍的人是否積極鼓勵正確行為、阻止錯誤行為?是否有人在做榜樣? |
| 結構能力 | 環境中是否有讓行為變得方便、容易、安全的因素(建置、預算等)? |
| 結構動機 | 是否有清晰且有意義的獎勵?短期獎勵是否與長期目標一致? |
要改變行為,需要同時解決所有相關因素。只改變其中一個,行為不會改變。例如,同時缺乏建置機器的預算和管理層對測試時間的認可,只解決其中一個問題是不夠的。
9.5 棘手問題與回答#
9.5.1 單元測試會增加多少時間?#
研究表明,提高程式碼品質可以增加生產力並縮短時程。寫測試確實會讓編碼變慢,但透過可維護性和更容易修復 bug 來彌補。
兩個功能的故事: 作者分享了一個大型企業的真實案例,比較了有測試和無測試的兩個團隊:
| 階段 | 無測試團隊 | 有測試團隊 |
|---|---|---|
| 實作(編碼) | 7 天 | 14 天 |
| 整合 | 7 天 | 2 天 |
| 測試與修 bug | 12 天 | 9 天 |
| 總發佈時間 | 26 天 | 24 天 |
| 上線後 bug 數 | 71 | 11 |
雖然單元測試讓實作時間加倍,但整體發佈時間反而更短。管理層最初只看編碼時間而認為試點失敗,卻忽略了整體效益。重點是要強調整體時間和品質,而非單一階段的時間。
9.5.2 QA 工作會受到影響嗎?#
單元測試不會消除 QA 職位。QA 工程師會收到已通過所有單元測試的應用程式,可以專注於更有趣的工作:找出邏輯性、應用層級的 bug,而非 UI 除錯。
- 單元測試是第一道防線
- QA 提供第二道防線 – 使用者接受度測試
- 如同安全措施,應用程式永遠需要不只一層的保護
9.5.3 如何知道單元測試真的有效?#
建立某種指標來衡量(參見 9.2.5 節)。如果可以衡量,就有辦法知道。而且,你會感受到它的效果。
測試覆蓋率趨勢報告是一個很好的起點,可以透過在建置流程中自動執行覆蓋率工具(如 NCover)來展示進度。
9.5.4 有證據證明單元測試有幫助嗎?#
目前沒有專門針對單元測試的研究,大多數相關研究是關於採用特定敏捷方法的整體效果。經驗性的證據可以從網路上找到:許多公司和同事取得了很好的結果,再也不想回到沒有測試的時代。
9.5.5 QA 部門為什麼還會發現 bug?#
QA 工程師的工作是從多個不同層面找出 bug,執行整合測試,這是單元測試無法涵蓋的。例如,各個元件在隔離狀態下通過單元測試,但在生產環境中一起運作時可能會出現問題。QA 還會從使用案例或完整場景的角度測試,發現邏輯性和驗收相關的 bug。
Glenford Myre 的研究顯示,開發者寫測試時並不是真正在找 bug,因此只找到了應用程式中約一半到三分之二的 bug。這意味著 QA 工程師的工作永遠都是必要的。
9.5.6 有大量無測試的程式碼,該從哪裡開始?#
1970 至 1990 年代的研究顯示,通常 90% 的 bug 集中在 20% 的程式碼中。找到問題最多的程式碼,從那裡開始。任何團隊都能告訴你哪些元件最棘手,可以加入一些指標(如每個類別的 bug 數量)來輔助判斷。
9.5.7 多語言環境下可行嗎?#
可行。用一種語言寫的測試有時候可以測試另一種語言的程式碼,特別是在 .NET 系列的語言混用情境下。每個團隊也可以用各自的語言寫測試,使用對應的測試框架。
9.5.8 軟硬體組合的開發怎麼辦?#
如果應用程式是軟硬體結合的,你需要為軟體部分撰寫測試。很可能已經有某種硬體模擬器可以利用。這絕對是可行的,許多公司都在這樣做。
9.5.9 如何確保測試本身沒有 bug?#
你需要確保測試在該失敗時失敗,該通過時通過。TDD 是確保這一點的好方法,因為你會先看到測試失敗,再讓它通過。
9.5.10 我的 debugger 就夠了,為什麼需要測試?#
- Debugger 對多執行緒程式碼幫助不大
- 你能確定自己的程式碼沒問題,但別人的程式碼呢?
- 別人怎麼知道你的程式碼能用,以及他們的修改沒有破壞任何東西?
- 程式碼生命週期的大部分時間都在維護模式,你需要確保它在出問題時能告訴人們
Curtis、Krasner 和 Iscoe 的研究顯示,大多數缺陷不是來自程式碼本身,而是來自人與人之間的溝通不良、不斷變化的需求,以及缺乏應用領域知識。即使你是世界上最好的程式員,如果別人告訴你錯誤的需求,你也會寫出錯誤的東西。
9.5.11 一定要用 TDD 嗎?#
TDD 是一種風格選擇,不是必須的。許多人覺得 TDD 高效且有益,但也有人覺得在程式碼之後寫測試就夠好了。如果對一次改變太多感到恐懼,可以分階段學習:
- 先學習單元測試,使用隔離框架(如 Typemock Isolator 或 JMockIt)
- 學習良好的設計技巧,如 SOLID 原則
- 學習測試驅動開發(推薦 Kent Beck 的 Test-Driven Development: By Example)
9.6 總結#
在組織中導入單元測試是本書許多讀者遲早會面臨的挑戰。關鍵要點:
- 做好準備 – 準備好回答可能被問到的棘手問題
- 不要疏遠盟友 – 確保能幫助你的人不會被你得罪
- 準備迎接硬仗 – 這可能是一場艱苦的戰鬥
- 理解影響力因素 – 行為改變需要同時解決多個層面的因素