久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

深度解讀 TDD(測試驅(qū)動開發(fā))

 逍遙302 2017-05-05

本文結(jié)構(gòu):

  • 什么是 TDD

  • 為什么要 TDD

  • 怎么 TDD

  • FAQ

  • 學(xué)習(xí)路徑

  • 延伸閱讀

什么是 TDD

TDD 有廣義和狹義之分,,常說的是狹義的 TDD,,也就是 UTDD(Unit Test Driven Development)。廣義的 TDD 是 ATDD(Acceptance Test Driven Development),,包括 BDD(Behavior Driven Test Development)和 Consumer-Driven Contracts Development 等,。本文所說的 TDD 指狹義上的 TDD,也就是「單元測試驅(qū)動開發(fā)」,。

TDD 是敏捷開發(fā)中的一項核心實踐和技術(shù),,也是一種設(shè)計方法論。TDD的原理是在開發(fā)功能代碼之前,,先編寫單元測試用例代碼,,測試代碼確定需要編寫什么產(chǎn)品代碼。TDD 是 XP(Extreme Programming)的核心實踐,。它的主要推動者是 Kent Beck,。

TDD 有三層含義:

  • Test-Driven Development,測試驅(qū)動開發(fā),。

  • Task-Driven Development,,任務(wù)驅(qū)動開發(fā),要對問題進(jìn)行分析并進(jìn)行任務(wù)分解,。

  • Test-Driven Design,,測試保護(hù)下的設(shè)計改善。TDD 并不能直接提高設(shè)計能力,,它只是給你更多機(jī)會和保障去改善設(shè)計,。

為什么要 TDD

傳統(tǒng)編碼方式 VS TDD 編碼方式

傳統(tǒng)編碼方式

  • 需求分析,想不清楚細(xì)節(jié),,管他呢,,先開始寫

  • 發(fā)現(xiàn)需求細(xì)節(jié)不明確,去跟業(yè)務(wù)人員確認(rèn)

  • 確認(rèn)好幾次終于寫完所有邏輯

  • 運行起來測試一下,,靠,,果然不工作,,調(diào)試

  • 調(diào)試好久終于工作了

  • 轉(zhuǎn)測試,QA 測出 bug,,debug,, 打補丁

  • 終于,代碼可以工作了

  • 一看代碼爛的像坨屎,,不敢動,,動了還得手工測試,還得讓 QA 測試,,還得加班...

TDD 編碼方式

  • 先分解任務(wù),,分離關(guān)注點(后面有演示)

  • 列 Example,用實例化需求,,澄清需求細(xì)節(jié)

  • 寫測試,,只關(guān)注需求,程序的輸入輸出,,不關(guān)心中間過程

  • 寫實現(xiàn),,不考慮別的需求,用最簡單的方式滿足當(dāng)前這個小需求即可

  • 重構(gòu),,用手法消除代碼里的壞味道

  • 寫完,,手動測試一下,基本沒什么問題,,有問題補個用例,,修復(fù)

  • 轉(zhuǎn)測試,小問題,,補用例,,修復(fù)

  • 代碼整潔且用例齊全,信心滿滿地提交

TDD 的好處

降低開發(fā)者負(fù)擔(dān)通過明確的流程,,讓我們一次只關(guān)注一個點,思維負(fù)擔(dān)更小,。

保護(hù)網(wǎng)TDD 的好處是覆蓋完全的單元測試,,對產(chǎn)品代碼提供了一個保護(hù)網(wǎng),讓我們可以輕松地迎接需求變化改善代碼的設(shè)計,。所以如果你的項目需求穩(wěn)定,,一次性做完,后續(xù)沒有任何改動的話,,能享受到 TDD 的好處就比較少了,。

提前澄清需求先寫測試可以幫助我們?nèi)ニ伎夹枨螅⑻崆俺吻逍枨蠹?xì)節(jié),,而不是代碼寫到一半才發(fā)現(xiàn)不明確的需求,。

快速反饋有很多人說 TDD 時,,我的代碼量增加了,所以開發(fā)效率降低了,。但是,,如果沒有單元測試,你就要手工測試,,你要花很多時間去準(zhǔn)備數(shù)據(jù),,啟動應(yīng)用,跳轉(zhuǎn)界面等,,反饋是很慢的,。準(zhǔn)確說,快速反饋是單元測試的好處,。

怎么 TDD

TDD 的基本流程是:紅,,綠,重構(gòu),。更詳細(xì)的流程是:

  • 寫一個測試用例

  • 運行測試

  • 寫剛好能讓測試通過的實現(xiàn)

  • 運行測試

  • 識別壞味道,,用手法修改代碼

  • 運行測試

你可能會問,我寫一個測試用例,,它明顯會失敗,,還要運行一下嗎?是的,。你可能以為測試只有成功和失敗兩種情況,,然而,失敗有無數(shù)多種,,運行測試才能保證當(dāng)前的失敗是你期望的失敗,。一切都是為了讓程序符合預(yù)期,這樣當(dāng)出現(xiàn)錯誤的時候,,就能很快定位到錯誤(它一定是剛剛修改的代碼引起的,,因為一分鐘前代碼還是符合我的預(yù)期的)。通過這種方式,,節(jié)省了大量的調(diào)試代碼的時間,。

TDD 的三條規(guī)則

  1. 除非是為了使一個失敗的 unit test 通過,否則不允許編寫任何產(chǎn)品代碼

  2. 在一個單元測試中,,只允許編寫剛好能夠?qū)е率〉膬?nèi)容(編譯錯誤也算失?。?/p>

  3. 只允許編寫剛好能夠使一個失敗的 unit test 通過的產(chǎn)品代碼

如果違反了會怎么樣呢?違反第一條,,先編寫了產(chǎn)品代碼,,那這段代碼是為了實現(xiàn)什么需求呢?怎么確保它真的實現(xiàn)了呢,?違反第二條,,寫了多個失敗的測試,,如果測試長時間不能通過,會增加開發(fā)者的壓力,,另外,,測試可能會被重構(gòu),這時會增加測試的修改成本,。違反第三條,,產(chǎn)品代碼實現(xiàn)了超出當(dāng)前測試的功能,那么這部分代碼就沒有測試的保護(hù),,不知道是否正確,,需要手工測試??赡苓@是不存在的需求,,那就憑空增加了代碼的復(fù)雜性。如果是存在的需求,,那后面的測試寫出來就會直接通過,,破壞了 TDD 的節(jié)奏感。

我認(rèn)為它的本質(zhì)是:分離關(guān)注點,,一次只戴一頂帽子在我們編程的過程中,,有幾個關(guān)注點:需求,實現(xiàn),,設(shè)計,。TDD 給了我們明確的三個步驟,每個步驟關(guān)注一個方面,。紅:寫一個失敗的測試,,它是對一個小需求的描述,只需要關(guān)心輸入輸出,,這個時候根本不用關(guān)心如何實現(xiàn),。綠:專注在用最快的方式實現(xiàn)當(dāng)前這個小需求,不用關(guān)心其他需求,,也不要管代碼的質(zhì)量多么慘不忍睹,。重構(gòu):既不用思考需求,也沒有實現(xiàn)的壓力,,只需要找出代碼中的壞味道,,并用一個手法消除它,,讓代碼變成整潔的代碼,。

注意力控制人的注意力既可以主動控制,也會被被動吸引,。注意力來回切換的話,,就會消耗更多精力,,思考也會不那么完整。使用 TDD 開發(fā),,我們要主動去控制注意力,,寫測試的時候,發(fā)現(xiàn)一個類沒有定義,,IDE 提示編譯錯誤,,這時候你如果去創(chuàng)建這個類,你的注意力就不在需求上了,,已經(jīng)切換到了實現(xiàn)上,,我們應(yīng)該專注地寫完這個測試,思考它是否表達(dá)了需求,,確定無誤后再開始去消除編譯錯誤,。

為什么很多人做 TDD 都做不起來?

不會合理拆分任務(wù)TDD 之前要拆分任務(wù),,把一個大需求拆成多個小需求,。也可以拆出多個函數(shù)來。

不會寫測試什么是有效的單元測試,,有很多人寫測試,,連到底在測什么都不清楚,也可能連斷言都沒有,,通過控制臺輸出,,肉眼對比來驗證。好的單元測試應(yīng)該符合幾條原則:

  • 簡單,,只測試一個需求

  • 符合 Given-When-Then 格式

  • 速度快

  • 包含斷言

  • 可以重復(fù)執(zhí)行

不會寫剛好的實現(xiàn)很多人寫實現(xiàn)的時候無法專注當(dāng)前需求,,一不小心就把其他需求也實現(xiàn)了,就破壞了節(jié)奏感,。實現(xiàn)的時候不會小步快走,。

不會重構(gòu)不懂什么是 Clean Code,看不出 Smell,,沒有及時重構(gòu),,等想要重構(gòu)時已經(jīng)難以下手了。不知道用合適的「手法」消除 Smell,。

基礎(chǔ)設(shè)施對于特定技術(shù)棧,,沒有把單元測試基礎(chǔ)設(shè)施搭建好,導(dǎo)致寫測試時無法專注在測試用例上,。

實例


寫一個程序來計算一個文本文件 words.txt 中每個單詞出現(xiàn)的頻率,。為了保持簡單,假設(shè):

  • words.txt 只包含小寫字母和空格

  • 每個單詞只包含小寫字母

  • 單詞之間由一個或多個空格分開

舉個例子,假設(shè) words.txt 包含以下內(nèi)容:


the day is sunny the thethe sunny is is


你的程序應(yīng)當(dāng)輸出如下,,按頻率倒序排序:


the 4is 3sunny 2day 1



請先不要往下讀,,思考一下你會怎么做。(思考 3 分鐘...)

新手拿到這樣的需求呢,,就會把所有代碼寫到一個 main() 方法里,,偽代碼如下:

main() {
   // 讀取文件
   ...
   // 分隔單詞
   ...
   // 分組
   ...
   // 倒序排序
   ...
   // 拼接字符串
   ...
   // 打印
   ...
}

思路很清晰,但往往一口氣寫完,,最后運行起來,,輸出卻不符合預(yù)期,然后就開始打斷點調(diào)試,。

這種代碼沒有任何的封裝,。這就是為什么很多人一聽到說有些公司限制一個方法不超過 10 行,就立馬跳出來說,,這不可能,,10 行能干什么啊,我們的業(yè)務(wù)邏輯很復(fù)雜...這樣的代碼存在什么樣的問題呢,?

  • 不可測試

  • 不可重用

  • 難以定位問題

好嘛,,那我們來 TDD 嘛,你說讀文件,,輸出控制臺的測試代碼要怎么寫,?當(dāng)然,我們可以通過 Mock 和 Stub 來隔離 IO,,但真的有必要嗎,?

有人問過 Kent Beck 這樣一個問題:


你真的什么都會測嗎?連 getter 和 setter 也會測試嗎,?


Kent Beck 說:公司請我來是為了實現(xiàn)業(yè)務(wù)價值,,而不是寫測試代碼。所以我只在沒有信心的地方寫測試代碼,。

那對我們這個程序而言,,讀文件和打印到控制臺都是調(diào)用系統(tǒng) API,可以很有信心吧,。最沒有信心的是中間那寫要自己寫的業(yè)務(wù)邏輯,。所以我們可以對程序做一些封裝,《代碼整潔之道》里說,,有注釋的地方都可以抽取方法,,用方法名來代替注釋:

main() {
   String words = read_file('words.txt')
   String[] wordArray = split(words)
   Map<String, Integer> frequency = group(wordArray)
   sort(frequency)
   String output = format(frequency)
   print(output)
}

這樣是不是就可以單獨為 splitgroup,,sort,,format 這些方法寫單元測試了呢?當(dāng)然可以, 它們的輸入和輸出都是很明確的嘛,。

等等,你可能會說,,不是測試驅(qū)動設(shè)計嗎,?你怎么開始做設(shè)計了?好問題,!


TDD 要不要做提前設(shè)計呢,?



Kent Beck 不做提前設(shè)計,他會選一個最簡單的用例,,直接開寫,,用最簡單的代碼通過測試。逐漸增加測試,,讓代碼變復(fù)雜,,用重構(gòu)來驅(qū)動出設(shè)計。在這個需求里,,最簡單的場景是什么呢,?那就是文件內(nèi)容為空,輸出也為空,。

當(dāng)然,,對于復(fù)雜問題,可能要一邊寫一邊補充新的用例,,但對于這種簡單的題目,,基本可以提前就想清楚用什么用例驅(qū)動去什么產(chǎn)品代碼。大概可以想到如下的用例:

  • '' => ''

  • 'he' => 'he 1',,一個單詞,,驅(qū)動出格式化字符串的代碼

  • 'he is' => 'he 1\r\nis 1',兩個不同單詞,,驅(qū)動出分割單詞的代碼

  • 'he he is' => 'he 2\r\nis 1',,有相同單詞,驅(qū)動出分組代碼

  • 'he is is' => 'is 2\r\nhe 1',,驅(qū)動出分組后的排序代碼

  • 'he is' => 'he 1\r\nis 1',,多個空格,完善分割單詞的代碼


Martin Fowler 的觀點是,,以前我們寫代碼要做 Big Front Up Design,,在開始寫代碼前要設(shè)計好所有細(xì)節(jié)。而我們有了重構(gòu)這個工具后,,做設(shè)計的壓力小了很多,,因為有測試代碼保護(hù),我們可以隨時重構(gòu)實現(xiàn)了。但這并不代表我們不需要做提前設(shè)計了,,提前設(shè)計可以讓我們可以和他人討論,,可以先迭代幾次再開始寫代碼,在紙上迭代總比改代碼要快,。我個人比較認(rèn)同 Martin Fowler 的做法,,先在腦子里(當(dāng)然,我腦子不夠用,,所以用紙畫)做設(shè)計,,迭代幾次之后再開始寫,這樣,,我還是會用最簡單的實現(xiàn)通過測試,,但重構(gòu)時就有了方向,效率更高,。

回到這個程序,,我發(fā)現(xiàn)目前的封裝不在一個抽象層次上,更理想的設(shè)計是:

main() {
   String words = read_file('words.txt')
   String output = word_frequency(words)
   print(output)
}

word_frequency(words) {
   String[] wordArray = split(words)
   Map<String, Integer> frequency = group(wordArray)
   sort(frequency)
   return format(frequency)
}

這時候,,又有兩種選擇,,有人喜歡自頂向下,有人喜歡自底向上,,我個人更傾向于前者,。


現(xiàn)在開始,只要照著 紅-綠-重構(gòu) 的循環(huán)去做就可以,。大部分 TDD 做不好,,就是沒有前面的任務(wù)分解和列 Example 的過程。想看 TDD 過程的話,,可以參考我做的直播(點擊「閱讀原文」查看),。


FAQ


為什么一定要先寫測試,后補測試行不行,?


行,,但是要寫完實現(xiàn)后,馬上寫測試,,用測試來驗證實現(xiàn),。如果你先手工測試,把代碼都調(diào)試好了,,再補單元測試,,你就會覺得很雞肋,還增加了工作量,。不管測試先行還是后行都可以享受到快速反饋,,不過如果測試先行,,你就可以享受另一個好處,使用意圖驅(qū)動編程減少返工,。因為你的測試代碼就是產(chǎn)品代碼的客戶端(調(diào)用者),,你可以在測試代碼里寫成你理想的樣子(方法名,參數(shù),,返回值等),,再去實現(xiàn)產(chǎn)品代碼,比起先寫實現(xiàn)后寫測試,,前者返工更少。


剛寫了一個測試,,還沒寫實現(xiàn),。明知道運行測試一定會報錯,為什么還要去運行,?


其實測試的運行結(jié)果并非只有通過與不通過兩種,,因為不通過時有很多種可能。所以在明知道一定失敗的情況下去運行測試,,目的是看看是不是報了期望的那個錯誤,。


小步快走確實好,但真的需要這么小步嗎,?


步子邁太大,,容易扯著蛋。練習(xí)的時候需要養(yǎng)成小步的習(xí)慣,,工作的時候可以自由切換步子的大小,。當(dāng)你自信的時候步子就可以大點,當(dāng)你不太自信的時候就可以立即切換到小步的模式,。如果只會大步,,就難以再小步了。


測試代碼是否會成為維護(hù)的負(fù)擔(dān),?


維護(hù)時也遵循 TDD 流程,,先修改測試代碼成需求變更后的樣子,讓測試失敗,,再修改產(chǎn)品代碼使其通過,。這樣你就不是在維護(hù)測試用例,而是在利用測試用例,。


為什么要快速實現(xiàn),?


其實是用二分查找法隔離問題,通過 hardcode 實現(xiàn)通過測試后,,就基本確定測試是沒有問題,,這時再去實現(xiàn)產(chǎn)品代碼,如果測試不通過,,就是產(chǎn)品代碼的問題,。所以小步快走主要是為了隔離問題,也就是你可以告別 Debug 了,。


為什么測試代碼要很簡單,?


如果一個測試失敗了,修復(fù)的時候是改測試代碼而不是產(chǎn)品代碼,,那就是測試代碼寫的不好,。當(dāng)測試代碼足夠簡單時,如果一個測試失敗了,,就有足夠信心斷定一定是產(chǎn)品代碼的問題,。


什么時候不適合 TDD?


如果你是做探索性的技術(shù)研究(Spike),,不需要長期維護(hù),,而且測試基礎(chǔ)設(shè)施搭建成本很高,那還是手工測試吧,。另外還有「可測試性極差的遺留系統(tǒng)」和「使用測試不友好的技術(shù)?!沟南到y(tǒng),做 TDD 可能得不償失,。

學(xué)習(xí)路徑


  1. 《有效的單元測試》

  2. 《代碼整潔之道》

  3. 《重構(gòu)》

  4. Transformation Priority Premise

  5. 《Test-Driven Development by Example》

  6. 《Growing Object-Oriented Software, Guided by Tests》


作者:Seaborn Lee,,程序員,培養(yǎng)者,。致力于用自己的行動,,影響他人做出積極的改變晨型人,,每日練習(xí)寫作,、繪畫、冥想,、即興幽默,。對「時間管理」和「習(xí)慣養(yǎng)成」有些心得。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多