TDD已死?如何進(jìn)行有效的TDD實(shí)踐 | IDCF

來源:ThoughtWorks洞見、劉冉的思辨悟
作者:劉冉
一、TDD已死?

當(dāng)前國內(nèi)對 TDD 的理解十分模糊,大部分人也沒有明確和有意識的去實(shí)施 TDD,因此許多人對此都有著不同的理解。其中最經(jīng)典的理解就是基于代碼的某個單元,使用 Mock 等技術(shù)編寫單元測試,然后用這個單元測試來驅(qū)動開發(fā),抑或是幫助在重構(gòu)、修改以后進(jìn)行回歸測試。而現(xiàn)在大部分反對 TDD 的聲音就是基于這個理解,比如:
- 工期緊,時間短,寫 TDD 太浪費(fèi)時間;
- 業(yè)務(wù)需求變化太快,修改功能都來不及,根本沒有時間來寫 TDD;
- 寫 TDD 對開發(fā)人員的素質(zhì)要求非常高,普通的開發(fā)人員不會寫;
- TDD 推行的最大問題在于大多數(shù)程序員還不會「寫測試用例」和「重構(gòu)」;
- 由于大量使用 Mock 和 Stub 技術(shù),導(dǎo)致 UT 沒有辦法測試集成后的功能,對于測試業(yè)務(wù)價值作用不大;
- ......
- 首先是先寫測試,這里的測試并不只是單元測試,也不是說一定要使用 mock 和 stub 來做測試。這里的測試就是指軟件測試本身,可以是基于代碼單元的單元測試,可以是基于業(yè)務(wù)需求的功能測試,也可以是基于特定驗(yàn)收條件的驗(yàn)收測試。
- 其次是幫助開發(fā)人員,主要是幫助開發(fā)人員理解軟件的功能需求和驗(yàn)收條件,幫助其思考和設(shè)計代碼,從而達(dá)到驅(qū)動開發(fā)的目的。

- ATDD(Acceptance Test Driven Development):驗(yàn)收驅(qū)動測試開發(fā),首先 BA 或者 QA 編寫驗(yàn)收測試用例,然后 Dev 通過驗(yàn)收測試來理解需求和驗(yàn)收條件,并編寫實(shí)現(xiàn)代碼直到驗(yàn)收測試用例通過。
- UTDD(Unit Test Driven Development):單元驅(qū)動測試開發(fā),首先 Dev 編寫單元測試用例,然后編寫實(shí)現(xiàn)代碼直到單元測試通過。這個就是現(xiàn)在很多人所謂的 TDD、實(shí)踐的 TDD、喜歡的 TDD、抱怨的 TDD,但是它卻只是真正意義上 TDD 的一部分而已。
TDD 金字塔二、TDD的實(shí)施和分層

TDD 的實(shí)施一般分為思維層面和技術(shù)層面。一般來說,思維層面上的實(shí)施成本較低、容易接受,但是缺點(diǎn)很多,比如難以傳遞、難以持續(xù)獲得快速反饋等;而技術(shù)層面上的實(shí)施一般成本較高、不容易被人接受,但是優(yōu)點(diǎn)更多,比如可以獲得快速反饋、更容易傳遞和協(xié)作等。而現(xiàn)實(shí)世界中 TDD 的實(shí)施一般分為三個階段,即無意識的 TDD、被動通過技術(shù)實(shí)現(xiàn)的 TDD、以及有意識和主動通過技術(shù)實(shí)現(xiàn)的 TDD。第一階段:無意識的 TDD對于軟件開發(fā)人員,當(dāng)他們拿到一個新的軟件需求時,首先會思考如何實(shí)現(xiàn),其中包括當(dāng)前軟件架構(gòu)、業(yè)務(wù)分解、實(shí)現(xiàn)設(shè)計、代碼分層、代碼實(shí)現(xiàn)等。然后通過思考和設(shè)計所得到的產(chǎn)出物來驅(qū)動代碼實(shí)現(xiàn),進(jìn)而在代碼實(shí)現(xiàn)中會思考如何通過一個或多個函數(shù)或者算法來實(shí)現(xiàn)業(yè)務(wù)邏輯。所以軟件系統(tǒng)的實(shí)現(xiàn)要先通過意識層面的思考,再進(jìn)行技術(shù)層面的工作。
當(dāng)開發(fā)人員思考和設(shè)計這些函數(shù)或者方法的時候,一般都會思考它們有哪些參數(shù),然后想象將這些參數(shù)換成真實(shí)的數(shù)據(jù)后傳遞進(jìn)去,會得到怎樣的返回值。好一點(diǎn)的開發(fā)人員會思考如何處理異常輸入和異常返回值。這類思考其實(shí)已經(jīng)是意識思維上的 TDD,它幫助開發(fā)人員先在大腦里面設(shè)計并驗(yàn)證代碼實(shí)現(xiàn),甚至幫助其重構(gòu)代碼。所以很多開發(fā)人員都在無意識的情況下做著 TDD。比如在一個銀行系統(tǒng)里面,開發(fā)人員拿到一個需求,需要開發(fā)一個通過手機(jī) APP 轉(zhuǎn)賬的功能。- 首先開發(fā)人員會基于當(dāng)前的軟件架構(gòu)思考:是開發(fā)一個全新的模塊來處理這個業(yè)務(wù)?還是基于當(dāng)前架構(gòu)中的某個模塊來添加代碼進(jìn)行處理?
- 當(dāng)確定架構(gòu)和設(shè)計之后,就開始思考具體的代碼實(shí)現(xiàn),比如類的設(shè)計、方法的設(shè)計或者函數(shù)的設(shè)計等。當(dāng)開發(fā)“將錢從原帳號轉(zhuǎn)出”這個功能前,開發(fā)人員會思考:這個功能需要支持當(dāng)錢從原帳號中轉(zhuǎn)出成功后,原帳號中的余額等于原始余額減去轉(zhuǎn)出金額。進(jìn)一步有些程序員還會設(shè)計一些用來驗(yàn)證功能的實(shí)例,比如帳號中的原始余額是 999.99,轉(zhuǎn)出 111.11,那么剩余的金額就應(yīng)該是 888.88。
- 在這樣思考之后,開發(fā)人員便開始根據(jù)自己大腦中的測試邏輯和用例來驅(qū)動和輔助開發(fā)過程。在代碼開發(fā)完畢之后還會想一些辦法來驗(yàn)證一下所實(shí)現(xiàn)的功能是否符合預(yù)期,比如人工使用之前的或者新的測試用例再測試一下。如果驗(yàn)證正確,就會認(rèn)為自己開發(fā)的功能正確了,并交給測試人員進(jìn)行測試。
TDD 倒三角所以,如果不希望技術(shù)層面上的 TDD 隨時倒塌,就需要把這個倒三角補(bǔ)全,才能更好的、長久的實(shí)施 TDD。第三階段:有意識和主動通過技術(shù)實(shí)現(xiàn) TDD為了大規(guī)模以及有效的實(shí)施 TDD,首先要突破思維意識的局限,認(rèn)識到 TDD 的普遍存在性和適用性,不要害怕和排斥 TDD 這種思維和開發(fā)模式。其次要主動學(xué)習(xí),并刻意練習(xí) TDD 的技術(shù)實(shí)現(xiàn),提升自己的技術(shù)能力,從而在技術(shù)層面能更容易的實(shí)現(xiàn) TDD,擺脫被動 TDD 的困境。其中學(xué)習(xí)的方法包括閱讀 TDD 相關(guān)的書籍和文章,書籍包括《測試驅(qū)動開發(fā)》、《重構(gòu)》、《BDD In Action》以及《系統(tǒng)思考》等,從而充分理解 TDD 優(yōu)點(diǎn)和局限。對于刻意練習(xí),一定要長時間堅持去做,讓其成為一種習(xí)慣。如果在項(xiàng)目中沒有合適的環(huán)境去練習(xí),還可以通過一些第三方的 TDD 練習(xí)系統(tǒng)去做刻意練習(xí),比如 Cyber-dojo( http://www.cyber-dojo.org/ )。只有大量的刻意練習(xí)才能讓你在真實(shí)的代碼編寫過程中去思考和理解 TDD,去運(yùn)用你通過學(xué)習(xí)得到的知識,最終才能做到有意識和主動的通過技術(shù)去實(shí)現(xiàn) TDD,TDD 的倒三角才能變成一個穩(wěn)定的磚塊,然后哪里需要往哪里搬。
TDD 磚塊三、TDD實(shí)踐

- 學(xué)好TDD靠多多練習(xí)就可以了,不用學(xué)習(xí)其理論知識;
- 開發(fā)應(yīng)該自己理解業(yè)務(wù),并提煉測試(需求)點(diǎn)來實(shí)施TDD;
- 開發(fā)只要做好TDD,就不需要其他人測試了;
- 軟件沒有做好就是因?yàn)門DD沒有做好;
- 等等。
- 首先,TDD不是銀彈,所以軟件沒有做好的原因是很多的,也不是靠TDD做好了就一定能做好。
- 其次,學(xué)習(xí)TDD的理論是非常重要的,而僅僅靠不斷練習(xí)并自悟的方法,對于大部分普通人來說是難以成功的。
- 最后,一般開發(fā)能做的較好的TDD,一般是UTDD,而對于ATDD,則需要相應(yīng)的業(yè)務(wù)分析,測試分析與設(shè)計的相關(guān)方法和技術(shù)。如果要求開發(fā)做好ATDD,則需要開發(fā)學(xué)習(xí)并掌握相應(yīng)的業(yè)務(wù)分析,測試分析與設(shè)計的相關(guān)方法和技術(shù)。而這對于開發(fā)也是非常大的挑戰(zhàn)。所以在真正的TDD實(shí)踐中,如果想大規(guī)模實(shí)施TDD,仍然需要相應(yīng)的分工才更可行,更容易實(shí)施。而提前充分學(xué)習(xí)并了解TDD實(shí)踐的相關(guān)理論,也是可以幫助實(shí)施人員更好的去實(shí)踐,少踩坑,從而正其思規(guī)其行。
- 測試前移(左移)的思維能力
- 軟件設(shè)計能力
- 業(yè)務(wù)和技術(shù)需求分析和任務(wù)拆分能力
- 測試用例分析和設(shè)計能力
- 自動化測試開發(fā)能力
- 代碼重構(gòu)和持續(xù)改進(jìn)能力
- 關(guān)注單元級別的代碼設(shè)計
- 測試用例需要明確的實(shí)例
- 清晰的單元完成標(biāo)志
- 最快的feedback周期
- 有效的減少開發(fā)過程中side effect引起的返工
- 可以幫助開發(fā)減少調(diào)式的成本
- 可以作為單元接口的使用文檔
- 關(guān)注業(yè)務(wù)價值,測試與需求一體化
- 明確的測試實(shí)例(SBE)而不是復(fù)雜的描述
- 清晰的功能完成標(biāo)志
- 更快的feedback周期,提早并頻繁溝通
- 消除誤解,減少返工
- 可視化的驗(yàn)收回歸測試
- 可以作為描述功能的活文檔
- 變紅:寫一個不通過的測試 (紅)?
- 變綠:寫實(shí)現(xiàn)代碼,使其剛好通過測試 (綠)
- 重構(gòu)
TDD實(shí)施-步驟
四、最后

- 不允許編寫任何產(chǎn)品代碼,除非目的是為了讓失敗的測試通過;
- 不允許編寫多于一個的失敗測試,編譯錯誤也是失敗;
- 不允許編寫多于恰好能讓測試通過的產(chǎn)品代碼,有效的減少返工。
想要提升敏捷DevOps技能,來場DevOps黑客馬拉松!想要尋找第二增長曲線實(shí)現(xiàn)創(chuàng)新增速,來場DevOps黑客馬拉松!2021年4月24-25日,IDCF DevOps黑客馬拉松走進(jìn)天府之國-成都趕緊報名參加吧~可自己報名,也可公司組團(tuán)參加哦!
評論
圖片
表情
