摘要:在軟件行業(yè)中,神仙打架的名場面,那就不得不提的是2014年的那場——測試驅(qū)動開發(fā)(TDD)之爭。
在歷史上有很多精彩絕倫的神仙打架,比如數(shù)學(xué)界的牛頓和萊布尼茨關(guān)于微積分的曠世之爭;比如量子物理中的愛因斯坦和波爾的紫禁之巔;比如足球里的梅西和C羅的旗鼓相當(dāng)難分高下;又比如滴滴和快滴之間觸目驚心的燒錢大戰(zhàn)……而在軟件行業(yè)中,也同樣有神仙打架的名場面,那就不得不提的是2014年的那場——測試驅(qū)動開發(fā)(TDD)之爭。
比賽的紅方是David Heinemeier Hansson,藍方是Kent Beck。David Heinemeier Hansson 由于名字較長簡寫成DHH,Ruby on Rails 正是出自于DHH之手。而這場打架還加入了“裁判”員——Martin Fowler,在比賽中Martin Fowler記錄了紅藍雙方的每一次組合拳、上勾拳、側(cè)踹、抱摔……總結(jié)如下:
紅方DHH觀點:
許多推動TDD的開發(fā)人員會讓你覺得:如果你不使用TDD的話,你的代碼就是骯臟的。
由單元測試開始驅(qū)動你的設(shè)計并不是一個好的主意。
TDD的概念流量交易“測試必須夠快”是目光短淺的。
對TDD的依賴會導(dǎo)致徹底忘記系統(tǒng)測試。
關(guān)注并且只關(guān)注單元模塊并不能有助于創(chuàng)建一套完美的系統(tǒng)。
100%的覆蓋率是愚蠢的。
程序員希望軟件是一門科學(xué),可是它并不是。它更像是創(chuàng)造性的寫作活動。
優(yōu)秀的軟件并不像工程學(xué)那樣,它更像寫作。清楚簡潔的寫作要優(yōu)于復(fù)雜晦澀的寫作。
清晰是有好處的,好到應(yīng)該將清晰性作為第一目標(biāo),而非測試覆蓋度或者測試速度。
成為一名優(yōu)秀的開發(fā)人員就像成為一名優(yōu)秀的作家一樣困難。
就像寫作一樣,成為優(yōu)秀的程序員的辦法就是以清晰為目標(biāo)從而大量編寫軟件、大量閱讀軟件。
藍方Kent Beck觀點:
DHH已將TDD委托給歷史垃圾堆。我很難過,不是因為我就把它從歷史的垃圾堆中拯救出來,而是因為現(xiàn)在我需要雇傭新技術(shù)來幫助我解決編程過程中的許多問題:
過度工程化。我傾向于“投入”我“知道”我“將需要”的功能。使一個紅色的測試變?yōu)榫G色(以及未來的測試列表)有助于我實現(xiàn)足夠的功能。我需要找到一個新的方法來保持專注。
API反饋。我需要找到一種新的方法來獲得關(guān)于我的API決策的快速反饋。
邏輯錯誤。我需要找到一種新的方法來抓住那些我很容易犯的討厭的測試錯誤。
文檔。我需要找到一種新的方式來傳達我對api的期望,并記錄我在開發(fā)過程中的想法。
感到不知所措。我真的會懷念如何使用TDD,即使我無法想象一個實現(xiàn),我?guī)缀蹩偰芟氤鋈绾尉帉憸y試。我需要找到一個新的方法,以便下一步上山。
將接口與實現(xiàn)思想分離。我傾向于用實現(xiàn)推測來污染API設(shè)計決策。我需要找到一種新的方法來分離這兩個層次的思維,同時在它們之間提供快速的反饋。
協(xié)議。我需要找到一個新的方法,精確地與一個編程伙伴關(guān)于我正在解決的問題。
焦慮。也許我最懷念的是TDD給我的瞬間“一切都好嗎?”按鈕。
我相信我會找到其他方法來解決這些問題。及時。疼痛會減輕的。再見TDD,老朋友。
神仙打架不虧是神仙打架,從那以后業(yè)界關(guān)于測試驅(qū)動開發(fā)的觀念也分成了兩派。一派主要來源自像國內(nèi)的一些互聯(lián)網(wǎng)等項目中聲音——需求的迭代和更新之快,要求公司或團隊能快速交付有價值的產(chǎn)品,而TDD對于很多開發(fā)人員來說無疑是帶來了繁重的工作壓力和交付壓力。甚至有人開玩笑話說:“ Deadline Driven Development 才是第一生產(chǎn)力 ”。
當(dāng)然也有人力挺TDD,“TDD并沒有死。很明顯,既然它有這么這么多的支持者,它怎么可能會死呢? 這就像在問,設(shè)計模式死了嗎?或者功能性自動化死了嗎?不,它并沒有死。而且它在將來任何時候都不會死亡。它將來可能會變成其他一些新的事物、甚至是一些更好的事物,但是它永遠不會死亡。所以讓我們跳過這一部分吧?!?/span>
關(guān)于測試驅(qū)動開發(fā)說了這么久,那么測試驅(qū)動開發(fā)到底是個啥呢?
測試驅(qū)動開發(fā)(TDD)是什么
測試驅(qū)動開發(fā),英文全稱Test-Driven Development,簡稱TDD,是一種不同于傳統(tǒng)軟件開發(fā)流程的新型的開發(fā)方法。 它要求在編寫某個功能的代碼之前先編寫測試代碼,然后只編寫使測試通過的功能代碼,通過測試來推動整個開發(fā)的進行。 這有助于編寫簡潔可用和高質(zhì)量的代碼,并加速開發(fā)過程。
Kent Beck:“測試驅(qū)動開發(fā)不是一種測試技術(shù)。它是一種分析技術(shù)、設(shè)計技術(shù),更是一種組織所有開發(fā)活動的技術(shù)”。
分析技術(shù): 體現(xiàn)在對問題域的分析,當(dāng)問題還沒有被分解成一個個可操作的任務(wù)時,分析技術(shù)就派上用場,例如需求分析、任務(wù)拆分和任務(wù)規(guī)劃等,《實例化需求》這本書可以給予一定的幫助作用。
設(shè)計技術(shù): 測試驅(qū)動代碼的設(shè)計和功能的實現(xiàn),然后驅(qū)動代碼的再設(shè)計和重構(gòu),在持續(xù)細微的反饋中改善代碼。
組織所有開發(fā)活動的技術(shù): TDD 很好地組織了測試、開發(fā)和重構(gòu)活動,但又不僅限于此,比如實施 TDD 的前置活動包括需求分析、任務(wù)拆分和規(guī)劃活動,這使得 TDD 具有非常好的擴展性。
測試驅(qū)動開發(fā)(TDD)的目標(biāo)
Kent Beck 在他的著作《Test-Driven Development》(見參考附錄)一書中提到:“代碼簡潔可用這句言簡意賅的話,正是 TDD 所追求的目標(biāo)”。
對于如何保證“代碼簡潔可用”可以使用分而治之的方法,先達到“可用”目標(biāo),再追求“簡潔”目標(biāo)。
可用: 保證代碼通過自動化測試。
代碼簡潔: 在不同階段人們對簡潔的理解程度也不一樣,不過遵循的原則差不多,例如 OOD 的 SOLID 原則(詳見參考附錄),Kent Beck 的 Simple Design 原則(詳見參考附錄)等。
雖然有很多因素妨礙我們得到整潔的代碼,甚至可用的代碼,無需征求太多意見,只需要采用 TDD 的開發(fā)方式來驅(qū)動出簡潔可用的代碼。
測試驅(qū)動開發(fā)(TDD)的規(guī)則
在TDD 的過程中,需要遵循的三項原則:
在編寫好失敗的單元測試之前,不要寫任何產(chǎn)品代碼。
只要有一個單元測試失敗了,就不要再寫測試代碼。無法通過編譯也是一種失敗。
產(chǎn)品代碼恰好能夠讓當(dāng)前失敗的單元測試成功通過即可,不要多寫。
測試驅(qū)動開發(fā)(TDD)的流程
測試驅(qū)動開發(fā)是一個過程,依賴于不斷重復(fù)極短的開發(fā)周期,這個周期也稱為“紅燈-綠燈-重構(gòu)”,如上圖。簡單的來說,基于TDD的三項原則,TDD的這種步驟(周期)如下:
添加一個小的測試
運行測試并查看失敗
對測試進行微小的改動通過測試
運行所有測試并看到其通過
通過重構(gòu)去掉重復(fù)部分
需要注意的是,不同階段有不同的目的,他們需要不同的解決方案,前二個階段需要很快地完成,以便知道新添加功能的狀態(tài)。為了達成這個目的,可以通過任何手段,因為僅在這時才這樣做,也是為了能快速完成好的設(shè)計。
測試驅(qū)動開發(fā)(TDD)的好處
TDD主要的好處主要包括了,確定性、重構(gòu)代碼、單元測試即文檔。
確定性。TDD提升了單元測試的覆蓋率,在每輪迭代產(chǎn)品都會新增代碼,如果有一套覆蓋率很高( 90% 或更高)的單元測試,那么只需執(zhí)跑一遍測試用例,那么能成功交付的把握就會比較大。反之,如果覆蓋率越低,越需要更多的人力去進行手動驗證。 在 kent Beck的《測試驅(qū)動開發(fā)》舉的例子中,正因有了TDD才有勇氣和老板說我們可以做!這就是TDD最強大的地方,它讓你擁有一套值得信賴的測試,打消你對修改代碼的恐懼。
重構(gòu)代碼。Martin Flower在他的《重構(gòu)》中也指出,完善的單元測試是他進行重構(gòu)的基石,從TDD的流程可以看到,重構(gòu)是TDD的一部分,運用TDD的同時也推動了代碼的重構(gòu)。
單元測試即文檔。在軟件行業(yè)里,人員的變動的很頻繁的,如果要盡快熟悉某個模塊的業(yè)務(wù)邏輯??次臋n?程序員寫的文章一般都不太容易看,而且文檔經(jīng)常會和代碼不同步,代碼修改了文檔沒跟著改的事情經(jīng)常發(fā)生??丛创a?看完也不一定知道為什么。如果這時候有一套非常完整的單元測試,那可能就是所有接手別人代碼的程序員的福音。首先,代碼不會撒謊,其次,測試用例明確告訴了你這個函數(shù)是做什么的,什么輸入對應(yīng)的都有什么預(yù)期輸出。單元測試就是最好的底層文檔,哪個專業(yè)人士不想提供這樣一份文檔呢?
此外,TDD還能夠促成良好的代碼設(shè)計。由于你先寫測試代碼,你會盡可能的讓代碼調(diào)用起來更加簡單方便,這也就促使你去考慮如何更好的設(shè)計代碼。以避免會出現(xiàn)一個函數(shù)里實現(xiàn)的功能過多,或者和其他代碼過于耦合而無法測試的情況。
當(dāng)然測試驅(qū)動開發(fā)除了好處以外,還有神仙打架中紅方代表DHH所提出的一些問題??偨Y(jié)來看,關(guān)于TDD的爭議可以大致從這幾個方面來看,軟件開發(fā)應(yīng)該由什么來驅(qū)動,測試的速度和覆蓋程度,以及設(shè)計思想層面等幾方面。從 辯證統(tǒng)一的角度來看,事物有兩個方面, TDD不一定能適用于所有的場景,同樣TDD的局限性在某些場景下也不見得是對的,如果想要能更好的適用于自身,不僅要拿捏好度的問題還要以敏捷的思想來應(yīng)對問題,比如不應(yīng)該盲目的制定100%或0%的測試覆蓋率,也不應(yīng)該固化開發(fā)步驟而不顧實際情況。
所以,在最后的神仙打架中,Kent Beck也表達了David的論述可能會讓TDD浴火重生、鳳凰涅槃的觀點,希望可以找到更加好的方法。但無論如何, 在我們實際工作中,不應(yīng)該因為某些 觀點成為我們接受或者拒絕它的理由。正所謂大道甚夷,而民好徑,作為敏捷開發(fā)中的一項優(yōu)秀實踐來看,TDD只有在真正使用過后才能評價是否已死的問題。那么你在踐行敏捷開發(fā)的時候,是否使用過TDD這種實踐呢,又或是踐行過其他一些敏捷開發(fā)的實踐呢,有沒有評測過你所在的項目中的敏捷開發(fā)的成熟度是如何的呢?
沒有那就對了!