TDD代码驱动测试基础
測試驅動開發(TDD)知識調研
文章目錄
- 測試驅動開發(TDD)知識調研
- TDD的核心目標
- 處理遺留代碼問題的核心法則如下。
- 不良測試的死亡漩渦(亦稱為 SCUMmy 周期)
- 警惕撤回邁入死亡漩渦的步伐
- 測試先行 -- FIRST原則
文章內容摘自 C++程序設計實踐與技巧——測試驅動開發 (Modern C++ Programming with Test-Driven Development:Code Better,Sleep Better) , Jeff Langr著
TDD的核心目標
TDD的核心目標就是盡可能頻繁地獲得較多的反饋。當你修改了一點代碼時,會想馬上知道改動是否正確。
你每做出一次小的改動,都需要運行所有的單元測試。TDD最大的好處是它能讓你在短時間內獲得有用的反饋。如果構建的測試運行得快,那么如前所述,完全有可能在幾秒內運行完所有的測試。如果等待的時間足夠短,那么經常運行所有的測試也是合理的。一旦反饋周期變長,TDD的威力就會減弱。獲取反饋的間隔時間越長,那么你寫的代碼出問題的可能性就越大。
通常在編寫代碼時很容易引入或大或小的問題。相比之下,每做一個小改動就運行一次測試,就能逐個解決這些問題。
處理遺留代碼問題的核心法則如下。
-
任何時候,只要可以就進行測試驅動。使用測試驅動方法時,有可能的話就將需要改動的代碼作為新的成員或新的類。
-
不要讓測試覆蓋率縮水。 非常容易出現的情況是,改動一點代碼,然后認為這只是一些簡單的代碼行,并對之不予過多考慮。如果沒有測試,每一行新加的代碼都會導致測試覆蓋率降低。
-
為了編寫測試,必須改動現有代碼! 由于對協作對象的依賴,大多數情況下不能方便地為遺留代碼編寫測試。在編寫測試前,需要一個打破依賴的方法。
-
可以在限制范圍內實施微小的代碼改動,這樣會降低風險。用一些技巧就可以手動地做出一些小而安全的代碼變換。
-
你編寫的每行代碼都有風險,甚至一個敲錯的字符都會引入潛在的缺陷,而這可能會浪費好幾個小時。盡可能少寫代碼,每敲擊一次鍵盤時都要想清楚。
-
堅持小的、增量的改動。 這在TDD中是奏效的。同樣,對遺留代碼也是如此。步子邁得太大會使你陷入困境。
-
一次只做一件事。在處理遺留代碼時,不要合并步驟或目標。例如,不要在重構的同時寫測試。
-
有些增量的改動可能會使代碼變得丑陋,要接受這一點。記住,一次只做一件事。 可能需要幾個小時做“正確”的事。不要等,現在就提交工作方案,因為這樣或許就不用花費過多時間。同時,不要過于擔心會違反一些設計原則。最終,你可能要抽出時間回過頭來整理代碼,或許你不會,但依然是進步了。你已經向正確的方向邁進了,而且也證明了所有代碼依然可以工作。
-
增量地修改代碼。 面對龐大的代碼庫時,僅僅為遇到的相關代碼編寫測試或許不能帶來巨大改觀。更重要的是你內心深知任何新加入的東西都需要經過測試。秉承測試第一的心態,你會開始從加入的測試中獲益。 每通過一個測試,可以回顧一下測試覆蓋的代碼區域。幾乎總能發現做一些小的、安全的重構工作的機會。同時,你也會發現在寫完一個測試后,再寫一個是如此容易。你可以在各個地方應用這種小的改善步伐而絲毫不影響產出,也將在困難重重的代碼庫中如履平地。
不良測試的死亡漩渦(亦稱為 SCUMmy 周期)
有時團隊開始使用TDD后,會在一段時期內得到良好的效果。而后事情開始慢慢被忽略,然后快速被忽略,最終決定放棄TDD。是什么導致了這個“討厭的測試死亡漩渦”?怎樣才能避免它呢?
這個問題不僅僅存在于TDD中。同樣也有“討厭的敏捷死亡漩渦”,偽敏捷的短迭代周期似乎在一段時間內產生了良好的效果。但1年或18個月后,團隊會對手頭上一堆混亂的場面十分吃驚。其結果就是敏捷方法被拋棄,背負著浪費時間的罵名。
Ben Rady和Rod Coffin在Agile2009峰會上名為“Continuous Testing Evolved”①的演講中描述了SCUMmy周期。縮寫SCUM描述了以下幾種導致退化的不良測試的特征:慢速(slow)、令人疑惑(confusing)、不可靠unreliable)、遺漏(missing)。下面是一個可能走向漩渦的情況(我在一些團隊中看到過幾次此類情境)。
團隊寫的測試大部分是集成測試。這些測試和不穩定或慢速依賴緊密耦合,如數據庫或其他外部API。雖然這會導致更慢的測試,但開始不會覺得有很大影響,因為仍可以在1~2分鐘內運行完幾百個測試。(想想“溫水煮青蛙”。)
測試變多后帶來的問題超越心理承受底線。現在運行完測試需要好幾分鐘。
開發人員運行測試的頻率變低,或者只運行一個測試子集。同時,團隊成員發現運行測試的問題增多。測試變得更長,需要更多的必要初始化,一旦出現問題,則需要更多的精力來進行理解和分析。其他問題也開始慢慢顯現,由于依賴單元測試控制之外的不穩定因素,測試會間歇性地失敗。開發人員發現測試經常上演“狼來了”,這意味著問題不是出在產品系統自身,而是測試設計。
開發人員刪掉測試。對于有問題的測試,本能反應就是禁用甚至刪掉。開發人員發現,刪掉測試比花費一個小時修復它們更容易。
代碼缺陷開始變多。剩下的測試很可能覆蓋不了足夠多的邏輯,在防止代碼缺陷方面價值較小。(在文檔化價值方面也大打折扣。)
團隊或管理層質疑TDD的價值。團隊試圖繼續,但很明顯這是徒勞的。
團隊放棄TDD。管理層記下這一明顯的失敗。
團隊該怎么辦呢?如果早知如此,那又何必當初呢?
理想狀況下,你已經從一些資料中學到了TDD,它要求限制每個測試的范圍,只測試一小段獨立的邏輯。以這種方式構建的系統不太可能卷入不良測試的死亡漩渦。而且,不是用了TDD就能神奇地產生高質量的系統。你和團隊必須不遺余力地去掉測試和產品代碼中的不良設計。這同樣要求團隊知道良好的測試和代碼是什么樣子的。
警惕撤回邁入死亡漩渦的步伐
下面的步驟將撤回邁入漩渦的步伐。如果你積極地觀察發生的事情,就可能不會再次在漩渦中越陷越深。
(1) 團隊寫的測試大部分是集成測試。學習怎樣編寫單元測試。重讀本書、參加培訓、雇一個教練、舉辦Dojos、更多地審查、更多地閱讀,等等。同時也要增加關于良好設計及代碼結構的知識。
(2) 測試變多后帶來的問題超越心理承受底線。將測試分為慢速和快速測試集。設立快測試的標準。(在一臺開發機上需要5毫秒或更少時間?)如果團隊成員向慢速測試集中加入一個測試,需要告知大家。學習重構測試及相關代碼要做的事情,以便讓測試變快。養成習慣,增量、但經常地嘗試將慢速測試改進為快速測試。
(3) 開發人員運行測試的頻率變低,或者只運行一個測試子集。 如果測試運行超過慢速閾值,那么將測試集標為失敗。(我最近為客戶成功地改動Google Test做到了這點。)這樣做會強化開發團隊認識到快速測試的重要性。
(4) 開發人員刪掉測試。監測測試覆蓋率。雖然建立覆蓋率目標的價值有待商榷(參見11.6節),你還是傾向于不斷增加或至少有一個穩定的覆蓋率數據。不幸的是,用良好的單元測試代替不良測試并獲得同樣的覆蓋率可能要費點功夫。但在養成正確的習慣前,最好花點時間往對的方向走,而非放棄。
(5) 代碼缺陷開始變多。對一個代碼缺陷的首要任務是寫一個測試。代碼缺陷為認識TDD實踐中的不足提供了機會。對于每個缺陷,要堅持在修復問題前寫一個運行失敗的單元測試。
(6) 團隊或管理層質疑TDD的價值。精益求精。堅信TDD實踐及其他實踐(如驗收測試、重構和結對編程)從長遠來看主要致力于產出高質量的軟件,而非僅僅減少代碼缺陷。確保明白這些實踐如何以及為什么和質量掛鉤,同時確保團隊成員以一種有助于達成高質量目標的方式踐行它們。缺乏對質量的關注將導致系統開發失敗——“不良測試的死亡漩渦”更不可饒恕。
(7) 團隊放棄TDD。不要坐以待斃!管理層很少會容忍再次邁向他們認為必定失敗的步伐。和其他事情一樣,你可能會因不當地使用TDD而導致失敗。但也有可能成功,并且是大獲成功,否則的話,我也不會大動干戈地寫下本書。由于未能正確地堅持一項技術而認為它本身不好,可是非常不好的!
測試先行 – FIRST原則
可以對照FIRST原則(由Brett Schuchert和Tim Ottinger提出)來審查。這個助記符可以提醒你TDD定義中的關鍵部分:測試先行。
FIRST可以分解為如下部分:
-
F 是快速(Fast);
-
I 是獨立(Isolated);
-
R是可重復(Repeatable);
-
S 是自我驗證(Self-verifying);
-
T 是及時(Timely)
總結
以上是生活随笔為你收集整理的TDD代码驱动测试基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ volatile关键字说明
- 下一篇: ros_openvino_toolkit