這篇關(guān)于測試驅(qū)動開發(fā)(TDD)的文章將幫助您熟悉這個開發(fā)周期并使其適應(yīng)您的編碼方法。
測試驅(qū)動開發(fā)(TDD)的概念由Kent Beck在2003年引入。沒有正式的定義,但Beck給出了TDD的方法和示例。TDD的目標是“編寫干凈的代碼”。
在TDD中,只跟隨一個經(jīng)驗法則:只有改變生產(chǎn)代碼,如果測試失敗。否則,只有重構(gòu)才能優(yōu)化代碼。對于更新的需求,將它們轉(zhuǎn)換為測試用例,添加這些測試,然后才編寫新代碼。
TDD是一個非常短的開發(fā)周期,并且是重復(fù)的??蛻粜枨筠D(zhuǎn)變?yōu)楦叨忍囟ǖ臏y試用例,并編寫和改進軟件以通過新測試。
測試驅(qū)動開發(fā)與極限編程中的測試優(yōu)先編程概念相關(guān),在短開發(fā)周期中提倡頻繁的軟件更新/發(fā)布,并促進廣泛的代碼審查,單元測試和增量功能的添加。
與TDD密切相關(guān)的概念是驗收測試驅(qū)動開發(fā)(ATDD),其中客戶,開發(fā)人員和測試人員都參與需求分析過程。TDD既適用于移動應(yīng)用程序開發(fā)人員,也適用于Web應(yīng)用程序開發(fā)人員,而ATDD則是確保需求定義明確的通信工具。
測試驅(qū)動開發(fā)(TDD)循環(huán)

讓我們從基礎(chǔ)知識開始,逐步了解TDD循環(huán),也稱為Red-Green-Refactor過程。
測試驅(qū)動的開發(fā)周期:
1.添加一個測試,肯定會失敗。(紅色)
在TDD中,首先根據(jù)測試用例添加軟件中的每個功能。為新功能或更新功能創(chuàng)建測試。要編寫測試,開發(fā)人員必須了解功能規(guī)范和要求。
這種做法將TDD與傳統(tǒng)的軟件開發(fā)方法區(qū)分開來,在編寫源代碼后編寫單元測試。通過這種方式,TDD使開發(fā)人員在編寫代碼之前關(guān)注需求。
2.運行所有測試??纯词欠裼腥魏螠y試失敗。
運行測試驗證測試工具是否正常工作并同時證明當添加的新測試失敗并且現(xiàn)有代碼失敗時,需要新代碼。
3.只寫足夠的代碼來通過所有測試。(綠色)
在這個階段編寫的新代碼可能并不完美,可能會以不相關(guān)的方式通過測試。這個階段唯一的要求是所有的測試都應(yīng)該通過。添加語句的一種可能方式是返回常量,并逐步添加邏輯塊以構(gòu)建函數(shù)。
4.運行所有測試。如果任何測試失敗,請返回步驟3.否則,繼續(xù)。
如果所有測試都通過,則可以說代碼符合測試要求,并且不會降低任何現(xiàn)有功能。如果任何測試失敗,則必須編輯代碼以確保所有測試都通過。
5.重構(gòu)代碼。(重構(gòu))
隨著代碼庫的增長,必須定期清理和維護。怎么樣?有幾種方法:
為方便傳遞測試而添加的新代碼可以移動到代碼中的邏輯位置。
必須消除重復(fù)。
必須設(shè)置對象定義和名稱以表示其用途和用法。
隨著更多功能的添加,功能變得冗長。它可以證明有利于拆分和仔細命名,以提高可讀性和可維護性。
由于所有測試都在重構(gòu)階段重新運行,因此開發(fā)人員可以確信該過程不會改變?nèi)魏维F(xiàn)有功能。
6.如果添加了新測試,請從步驟1開始重復(fù)。
采取小步驟,在每次測試運行之間進行少至1到10次編輯。
如果新代碼沒有快速滿足新測試,或者其他不相關(guān)的測試意外失敗,則撤消/恢復(fù)為工作代碼,而不是進行大量調(diào)試。
使用外部庫時,重要的是不要使增量小到僅僅測試庫本身,除非是測試庫是否過時/不兼容,錯誤或功能不完整。
TDD周期中的常見做法
在這部分中,我將為您提供一個快速的TDD常用實踐演練,它將幫助您更好地編寫代碼。
小單位
單元是一個類/模塊,它是一組密切相關(guān)的函數(shù),通常稱為模塊。保持較小的單元增加了諸如更容易測試和調(diào)試的好處。
測試結(jié)構(gòu)
由于您將始終運行單元測試,因此應(yīng)用以下測試結(jié)構(gòu)非常重要:
設(shè)置:使系統(tǒng)或被測單元(UUT)進入運行測試的必要狀態(tài),確保系統(tǒng)準備好進行測試。
執(zhí)行:在目標上運行測試并監(jiān)控所有返回值和輸出,確保執(zhí)行路徑是您要定位的路徑。
驗證:斷言/確保結(jié)果正確。這是聲明測試是否通過/失敗的重點。
清理:將測試系統(tǒng)恢復(fù)到原始狀態(tài)。這允許立即執(zhí)行另一個測試。
要避免的做法
決定論?- 確保測試是確定性的。對API調(diào)用或系統(tǒng)日期/時間等事件的依賴性可能導(dǎo)致測試失敗,即使代碼沒有更改也是如此。
如果可能,請避免執(zhí)行測試執(zhí)行順序,并允許隨機執(zhí)行測試。同樣,避免讓測試依賴于先前或其他測試結(jié)果。
測試精確的執(zhí)行行為時間或性能。
不要開發(fā)那些評估超出預(yù)期的測試用例(“無所不知的神諭”)。
不要設(shè)計執(zhí)行時間要長得多的測試。
個人實踐
保持每項測試僅關(guān)注驗證它所需的結(jié)果。
在非實時系統(tǒng)中,開發(fā)與時間相關(guān)的測試以實現(xiàn)執(zhí)行容差。允許延遲執(zhí)行的5%到10%的余量以減少測試期間漏報的可能性是常見的做法。
將測試代碼視為與生產(chǎn)代碼相同。這提高了代碼質(zhì)量和穩(wěn)健性。
在可行的情況下將測試分成更小的測試。
作為一個團隊,請檢查您的測試和測試實踐,以分享有效的技術(shù)并捕捉壞習慣。
高級實踐
驗收測試驅(qū)動開發(fā)(ATDD)具有先進的TDD實踐,開發(fā)團隊的目標是滿足客戶定義的驗收測試??蛻艨梢允褂米詣訖C制來確定軟件是否滿足其要求。
測試驅(qū)動開發(fā)(TDD) - 優(yōu)點和缺點
優(yōu)點
在TDD中編寫測試會強制您考慮用例,并提高工作效率。
即使考慮到基于編寫單元測試的代碼量,總體實現(xiàn)也將更短且更少的錯誤。
調(diào)試變得更容易。
如果遵循常見的TDD實踐,則開發(fā)的代碼是模塊化的,靈活的和可擴展的。
每次增量更新都會自動回歸檢測。
自動化測試非常徹底。由于編寫的代碼不會超過通過失敗測試所需的代碼,因此這些自動化測試往往涵蓋每個代碼路徑。
更簡單的文檔,單元測試是自我記錄,更易于閱讀和理解。您應(yīng)始終以描述性方式記錄源/生產(chǎn)代碼。
限制
當需要功能測試時,TDD不能很好地完成,例如GUI設(shè)計。
當開發(fā)人員自己編寫單元測試時,測試可能會與代碼共享相同的盲點。
有時,大量的通過測試會產(chǎn)生錯誤的安全感,導(dǎo)致集成測試期間的測試活動減少,從而可能導(dǎo)致問題。
測試成為維護開銷的一部分。寫得不好的測試可能會進一步導(dǎo)致維護或更新的成本增加。
TDD期間實現(xiàn)的詳細程度無法在?以后輕松重新創(chuàng)建。
實踐中的測試驅(qū)動開發(fā)
適用于大型系統(tǒng)
對于大型系統(tǒng),測試具有挑戰(zhàn)性,并且需要具有明確定義的組件的模塊化架構(gòu)。必須滿足的一些關(guān)鍵要求是:
高內(nèi)聚確保每個模塊提供一組相關(guān)功能,使相應(yīng)的測試更易于維護。
低耦合允許對模塊進行隔離測試。
場景建模
在場景建模中,構(gòu)建了一組序列圖,每個圖表都集中在單個系統(tǒng)級執(zhí)行場景中。它為創(chuàng)建響應(yīng)輸入的交互策略提供了極好的工具。
每個場景模型都作為組件將提供的功能的一組要求。場景建模有助于在復(fù)雜系統(tǒng)中構(gòu)建TDD測試。
代碼可見性和安全性
區(qū)分測試和生產(chǎn)之間的代碼非常重要。單元測試套件必須能夠訪問要測試的代碼。但是,信息隱藏和封裝以及模塊分離等標準的設(shè)計不得受到損害。
在面向?qū)ο蟮脑O(shè)計中,測試仍然無法訪問私有數(shù)據(jù)成員和方法,并且需要額外的編碼?;蛘?,可以在源代碼中使用內(nèi)部類來包含單元測試。此類測試黑客不應(yīng)保留?在生產(chǎn)代碼中。TDD從業(yè)者經(jīng)常爭論是否應(yīng)該測試私人數(shù)據(jù)。

結(jié)論
在本文中,我們概述了測試驅(qū)動開發(fā)(TDD)。我們看到了TDD的優(yōu)勢和局限,以及與TDD相關(guān)的實踐,涵蓋了開始采用TDD周期所需的知識。