實施TDD時的常見問題

2022-02-21 13:34:09 字數 3758 閱讀 9691

在infoq最近發表的一篇文章《實施tdd時的常見問題》中,

chad meyers提出了關於tdd實施的問題,如下所示:

我該容忍多大限度的預先設計?你怎麼知道應該何時停止(也就是說,「當人們開始討論演算法,就是該測試的時機了」)?

對於象「我心裡清楚我們需要這個」這類東西——我們該如何處理(例如,在控制台main()方法中加上乙個try/catch?)

編寫測試時,為了讓**編譯通過,你不得不寫下一兩個介面,乙個實體類,在類中還有一些notimplementedexceptions等等。這一步該走多遠?

如果在測試乙個使用者故事期間,你發現先前的預先設計有問題,你是會馬上停下來跟你的搭檔討論,做該做的事,然後繼續;還是折返回去,在當前的故事中採用完整的測試優先模式?

在處理乙個新的使用者故事時,你發現針對前乙個故事所編寫的測試已經不再體現需求。你是否會立刻重構那個測試,還是把它標記為「忽略」,等你完成當前的故事再回過頭去處理那個被忽略的測試?還是有其它做法?

如果新的使用者故事要求對某個已有的測試做出輕微調整,你是會調整它,還是會寫乙個新測試,把舊的扔掉(也就是,「不許更改現有的測試**!」,或者「只有出現小的編譯問題時才準動它,否則就別碰!」)?

如果你構建了模型並且通過了測試,但是你發現這個設計很幼稚,而且即將要做的使用者故事肯定會對其進行重大修改,並生成乙個新的完全不同的模型。你是應該退

一步,考慮進行大型重構嗎?還是應該繼續修修補補,調整現有的模型,儘管這個模型最終會被目前看來顯然超出該使用者故事範圍的工作所改進?

derick bailey分享了他對chad這些問題的看法。以下是他的回答的摘錄:知道自己應該構建什麼就行。

這個問題實際上應該根據你的實際專案來回答。我最近做了乙個企業整合專案,裡面用到了我們的核心維護系統,然後新增了一些離線操作功能。在這個專案中,我

沒有做太多的預先設計。我知道系統需要能夠在離線模式下操作,我還知道即使是沒人登入計算機或者執行這個軟體時,它也需要能夠執行一些操作,我知道它需要

跟主維護系統進行雙向通訊,用來執行與維護相關的的一定數量的任務。我從一些設想/預先設計開始動手——構建了乙個windows

service來執行核心程序,構建了乙個客戶端-伺服器架構來託管ui,在乙個訊息系統上為通訊提供支援。除了這些算是預先設計以外,在構建軟體的過程

中都是其他各項任務的功能需求來驅動專案設計的進展。

最近,我與乙個為實驗室開發某系統的團隊一起工作時,有了截然相反的體驗。我們有源於客戶的需求,客戶所要求的流程,以及對客戶手中資料進行轉換的需要;

我們需要乙個大規模的預先設計,用以判斷我們的解決方案可以滿足資料轉換和功能需求。系統的核心設計是預先完成的,但是具體實現還是在開發過程中慢慢成

型。這是乙個讓我在心裡做鬥爭的問題,回答它需要視出現這個問題的時機。我並不希望新增特性和功能,除非我知道現在需要它們——當這個問題涉及到應用程式的域模型和核心基礎架構時。然而,對於類似於console.writeline(ex)之類的問題,要將這些功能新增到應用程式的main方法中,我認為沒有什麼好爭論的。畢竟——你並不會對應用程式的main方法做單元測試……你會對它進行整合測試。

對於實現介面,我並不希望手動地編寫樁物件。相反,我會試著盡可能使用rhinomocks,這樣就能避免這一問題。我過去習慣於在任何時候都要編寫樁**,其中包括十幾個未實現的方法——結果導致我的單元測試難以維護,同時還為確定的單元測試中確定的方法提供了大量的重複樁物件。

你應該只新增你需要的方法。如果你擁有乙個真實的物件,它包含了將要丟擲的未實現的異常,你可能要檢查一下這個方法,看它是否能夠從**庫中刪除。

這一問題仍然要視具體的問題而定。

我花了很多時間和我的合作者開展結對程式設計。我們不斷遇到這樣乙個場景,即下乙個功能需求會導致現有設計的改變。如果發生了這樣的事情,我們只會對我們需要的部分進行修改。我們會為某些新功能編寫新的單元測試,如果之前的設計使得我們的單元測試無法通過,我們就會返回修改之前的設計——對在修改範圍內的單元測試進行修改與更新。我們要將在這種情況之下的單元測試看作是「必做列表」中的內容。當我們修改已經完成的設計時,我們通常會忘記這個已經修改的範圍,或者忘記我們試圖滿足的新的功能需求。所幸,通過對整個單元測試套件的完整執行,可以顯示被破壞的測試,提醒我們需要修改。

在結對程式設計期間,我們有很多機會獲得新的需求,回顧我們的設計與實現,通過對這一特定需求的理解,認識到整個模型是錯誤的,我們需要從頭編寫。此時,我們做了許多人想都沒有想過的事情——我們刪除了系統中的所有類和單元測試,然後從頭再來。通過這種方法獲得的經驗教訓以及對其他需求的理解,可以引導我們開始乙個新的設計,它能夠考慮我們需要的功能——但是,我們仍然能夠在我們需要的基礎上,編寫現有的功能。我們雖然沒有建立「未來的**」,但通過額外的介面和抽象提供了可擴充套件性……

我已經反覆強調了這樣乙個迴圈:「設計、修改、重新設計、修改、重新開始構建、修改」。根據我的經驗,最終的結果值得我們這樣去做——系統中更少的bug,更加準確地表示業務需求的模型,更好的擴充套件性和靈活性,從而允許在未來作出修改。

盡早地修復它。如果你現在不修復它,你可能會在以後做更多的工作,因為會有一些新的內容與這些錯誤的內容產生互動,從而使得你必須同時修改錯誤的內容與新的內容。如果你現在就修改錯誤的內容,就不必在後面重寫新的內容了。

如果你正在修改乙個測試所指的含義(例如,對測試進行重新命名,使得它準確表達測試的內容與原因),那麼,你可以自動地刪除乙個舊的測試,建立乙個新的——即使你只是從語義上對它進行刪除,你僅僅只是進行重新命名以及修改很少的細節。如果測試代表的含義仍然是相同的,你就可以只修改某些實現細節(因為根據根據問題4,你修改了設計),然後保留這個測試,而只是修改實現去滿足新實現的需求。

參見問題4的回答。

此外, derick bailey還談到了其他有關tdd的問題,例如對斷言的比較,對setup方法的分析,對重構工具的認識等。

對於這7個問題,我的回答如下:

1、預先設計的度並沒有乙個準確的值。首先對於乙個系統而言,我們應該從領域與架構的角度分析技術關鍵點,並對這些關鍵點進行預先設計,是非常有必要的。此外,對於系統的infrastructure的內容,我們也需要預先設計,除非這些內容是已經存在的。其實,如果採用領域驅動設計的方式開展對領域邏輯的分析,那麼何時開始做測試,應該沒有太大的歧義,它自有一套規則需要我們遵循。tdd並不是完全排除設計的作用,利用測試來驅動開發,其實同時也代表我們可以利用測試來驅動設計。因此,我的意見是如果將系統各個組成部分看作是package或者component,那麼理想地tdd起點應該是在package或component被劃分好之後,我們就可以利用單元測試來驅動我們對領域模型的思考與編碼實現了。

2、這個問題沒有什麼好說。如果是問題中的例子,我想沒有什麼好爭論的,正如derick bailey所說,我們確實不會對main方法進行單元測試。我贊成derick的意見。

3、顯然,如果能夠利用工具來模擬樁物件,何樂而不為?如果測試**依賴的介面和實體類,本身就是我們在實現系統時就需要的,那麼實現它們算不上是無用功,此時,我贊成先去定義或者實現它們。不過在定義和實現它們的同時,需要考慮到這些定義不一定完整與準確,我們還要考慮隨時會對其進行修改,修改後,還要必須檢驗你的測試**。

4、既然發現有問題,為什麼要暫時放過,以觀後效呢?我的思想一貫都是要將錯誤扼殺在搖籃中。否則,因為一時偷懶放過的這個問題,往往會在後面給我們帶來無窮無盡的煩惱。如果你不能確認這些錯誤,拿出來討論是乙個比較好的辦法。

5、與問題4相似。

6、具體看修改的範圍。如果測試需要修改的不會影響依賴它的測試用例,即不會修改它的外部介面,那麼只需要修改部分實現**就可以了。沒有必要去專門編寫乙個新的測試用例。如果涉及到對外部介面的需要,可以考慮重構。例如derick提到的重新命名的問題。如果重構不能解決,那就可以編寫乙個新的測試。

7、很顯然,與問題4相同。

實施TDD時的常見問題

作者amr elssamadisy譯者李劍發布於 2008年3月12日 上午1時17分 社群 agile 主題敏捷實施,單元測試 標籤測試驅動開發 如果你剛接觸tdd不久,可能一些常見的問題正在困擾著你 chad meyers寫下了一些他在開始接觸tdd實踐時碰到的 疑惑和問題,它們應該都是比較常見...

實施TDD時的常見問題

在infoq 發表的一篇文章 實施tdd時的常見問題 中,chad meyers提出了關於tdd實施的問題,如下所示 我該容忍多大限度的預先設計?你怎麼知道應該何時停止 也就是說,當人們開始討論演算法,就是該測試的時機了 對於象 我心裡清楚我們需要這個 這類東西 我們該如何處理 例如,在控制台mai...

Dynamics 365 實施常見問題及避免

專案管理中,解決問題的方法沒有對錯,只有是否適合自己,以下經驗僅僅是個人的觀點,分享出來是為了跟同行進行交流,給初學者在專案實施過程中一些參考案例及思路。經歷過erp的實施後發現,大部分的專案實施都很辛苦,尤其上線前後,顧問們都加班加點的幹活,但是上線後仍然發現有很多問題,我見過和聽過的問題主要有以...