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