2010-02-11 89 views
25

我最近使用TDD完成了一個項目,發現這個過程有點噩夢。我喜歡先寫測試並觀察代碼的增長,但只要需求開始改變,我開始重構,我發現我花了更多時間重寫/修復單元測試,而不是編寫代碼,實際上需要更多的時間。可維護單元測試

我感覺,當我正在經歷這個過程時,在應用程序完成後做測試會容易得多,但如果我這樣做了,我會失去TDD的所有好處。

那麼有沒有關於編寫可維護的TDD代碼的命中/提示?我正在閱讀Roy Osherove的The Art Of Unit Testing,有沒有其他資源可以幫助我?

感謝

+0

工具只需修改一個例子,我決定有一天,我深深的代碼屬性的命名是錯誤的,所以我把它改成一個更好的。這個屬性用於大約30個不同的測試,所以我必須經過每個測試並手動更改,這需要很長時間。我尋找一些實用的命中/提示,像resharper,使用var關鍵字等。 – 2010-02-11 11:26:14

+2

@Lee Tevail:你是說(a)你的IDE沒有全局搜索和替換,(b)TDD是你的IDE的根本原因好可憐? – 2010-02-11 11:39:04

+0

我也不討厭TDD,我只是想找辦法更有效地做事。 – 2010-02-11 11:42:28

回答

16

實踐

這需要一段時間來學習如何寫像樣的單元測試。一個困難的項目(更像項目)並不奇怪。

xUnit Test Patterns已推薦的書很好,我聽說過你正在閱讀的書的好處。

至於一般建議,它取決於你的測試很難。如果他們經常破產,他們可能不是單元測試,更多的是集成測試。如果它們難以建立,SUT(被測系統)可能顯示出過於複雜的跡象,並且需要進一步的模塊化。名單繼續。

我住的一些建議是遵循AAA規則。

安排,行動和斷言。每個測試應該遵循這個公式。這使得測試可讀,易於維護,如果它們確實中斷時。

設計仍然是重要的

我實踐TDD,但任何代碼寫之前,我抓住一個白板,信手寫了。雖然TDD允許您的代碼發展,但一些前期設計總是有益的。那麼你至少有一個起點,從這裏你的代碼可以由你寫的測試驅動。

如果我正在執行一項特殊的難題,我會製作一個原型。忘掉TDD,忘記最佳實踐,只需打開一些代碼即可。顯然這不是生產代碼,但它提供了一個起點。從這個原型開始,我會考慮實際的系統,以及我需要的測試。

查看Google Testing Blog - 這是我開始TDD時的轉折點。米斯科的文章(和網站 - 尤其是Guide to Testable代碼)非常出色,並且應該指向正確的方向。

+0

+1。喜歡參考米斯科的文章。我認爲他們也是我的轉折點。 – 2010-02-11 11:27:33

2

是的,有一整本書叫xUnit Test Patterns是處理這個問題。

這是一本Martin Fowler簽名書,因此它具有經典圖案書的所有圖案。無論你喜歡與否,都是個人品味的事情,但我認爲它非常寶貴。

無論如何,問題的關鍵是你應該把你的測試代碼當作你的生產代碼來對待。首先,您應該堅持原則,因爲這樣可以更輕鬆地重構您的API。

2

你是否在自由使用接口,依賴注入和嘲諷?

我發現使用DI框架(例如ninject)設計接口然後注入這些接口的實現使模擬出應用程序的某些部分變得容易很多,這樣您就可以單獨地正確測試組件。

這樣可以更輕鬆地在一個區域中進行更改,而不會影響其他區域,或者如果需要更改這些更改,則可以更新接口並一次處理每個不同的區域。

7

「一旦需求開始變化,我開始做重構我發現,我花更多的時間重寫/定影單元測試」

所以呢?這是一個什麼問題?

您的要求已更改。這意味着你的設計必須改變。這意味着你的測試必須改變。

「我花了更多的時間來重寫/修改單元測試,而不是寫代碼,實際上花的時間更多。」

這意味着你正在做它的權利。要求,設計和測試影響都在測試中,您的應用程序不需要太多改變。

這就是它應該工作的方式。

回家快樂。你已經完成了這項工作。

+0

這樣的思考正是我不接受TDD的原因。程序員的工作不是編寫測試,而是編寫有效的代碼。如果驗證代碼比創建實際代碼要花費更長的時間,我認爲在這個過程中存在嚴重缺陷。 – erikkallen 2010-02-11 11:31:07

+7

@erikkallen:程序員的工作是「產生工作,良好的代碼」*,在其中你完全有信心*。我無法強調足夠的信心。測試是提高代碼信心的好方法。它*應該花費很長時間來驗證代碼是否正確。 TDD不會改變這一點。沒有什麼改變。信心需要很多照顧 - 無論是大量的測試還是精心打造的證明。或兩者。 – 2010-02-11 11:37:58

3

這聽起來像你的單元測試是脆弱和重疊的。理想情況下,單個代碼更改應該隻影響一個單元測試 - 對測試與功能進行一對一匹配,其他測試不取決於給定功能。這可能有點過於理想化;在實踐中,我們的許多測試都會重新使用相同的代碼,但需要牢記。當一個代碼更改影響很多測試時,這是一種氣味。另外,關於您重命名的具體示例:找到一個工具,可以爲您自動執行這些重構。我相信Resharper和CodeRush都支持這種自動重構;它比手動方法更快,更簡單,更可靠地進行重構。

爲了更好地學習您的IDE,沒有什麼比與其他人搭檔。你們都會學習;你們都會開發新的技能 - 而且不需要很長時間。幾個小時將大大增加您對該工具的舒適度。

+0

+1用於推薦配對學習IDE。我喜歡每隔幾個星期左右花半天時間,並僅花時間學習關於IDE或其他工具的新知識。長期來看,它會帶來巨大的回報。 – 2010-02-15 05:29:38

2

我想你可能想在測試和編碼之間取得體面的平衡。

當我開始一個項目時,由於要求和目標一直在改變,所以我根本沒有寫任何測試,因爲正如你所觀察的那樣,要不斷修復測試需要很多時間。有時候我只是在評論中寫下「這應該被測試」,以便我不會忘記測試它。

在某個時間點,您感覺您的項目正在形成。這是重度單元測試派上用場的時刻。我儘可能寫。

當我開始進行大量的重構工作時,在項目再次安頓下來之前,我對測試不感興趣。我也放了一些「測試這個」的評論。重構結束後,是時候重新編寫所有失敗的測試(也許還會拋棄其中的一些測試,當然也會寫一些新測試)。

用這種方式編寫測試真的很高興,因爲它確信你的項目已經達到了一個里程碑。

5

我是單元測試的狂熱粉絲,但在我最近的項目中遇到過TDD(或基本單元測試)的問題。在進行實施後評審後,我發現我們(我和團隊的其他成員)在實施/理解TDD和單元測試時遇到了兩個主要問題。

第一個問題是我們面臨的問題是我們並不總是把我們的測試當成一等公民。我知道這聽起來像是我們反對TDD的哲學,但是在我們完成了大部分最初的設計之後,我們的問題就出現了,並且急於進行實時更改。不幸的是,由於時間限制,項目的後期部分變得匆忙,我們陷入了編寫代碼之後編寫測試的陷阱。當壓力安裝的工作代碼被檢查到源代碼控制,而不檢查單元測試是否仍然通過。誠然,這個問題與TDD或單元測試沒有任何關係,而是緊迫期限,平均團隊溝通和糟糕領導力的結果(我要在這裏責怪自己)。

當對失敗的單元測試進行更深入的研究時,我們發現測試過多,特別是考慮到時間限制。我們沒有使用TDD並將測試集中在高回報的代碼上,而是使用TDD併爲整個代碼庫編寫測試。這使得我們的單元測試代碼的比例比我們可以維護的代碼高得多。我們(最終)決定只使用TDD並編寫可能會改變的業務功能的測試。這減少了我們維持大部分測試的需要,這些測試大部分很少(或從不)改變。相反,我們的努力更專注於爲我們真正關心的應用程序的各個部分提供更全面的測試套件。

希望能從我的經驗中學習,並繼續開發TDD或至少仍然爲您的代碼開發單元測試。就我個人而言,我發現以下鏈接對幫助我理解諸如選擇性單元測試等概念非常有用。

1

首先,重構不破的單元測試。你有申請the book的重構嗎?這可能是因爲你的測試正在測試實現而不是行爲,這可能解釋了它們爲什麼會崩潰。

單元測試應該是黑匣子測試,測試單元做什麼,而不是測試它是如何做的。

0

你使用的是一個好的IDE嗎?當我第一次接受單元測試時,我正在問自己和幾年前一樣的問題。那時候,我用Emacs,findgrep的組合來做重構。這很痛苦。

值得慶幸的是,一位同事讓我感到頭痛,並說服我嘗試使用「現代工具」,他用白話意思是Intellij IDEA。 IDEA是我個人的偏好,但Netbeans或Eclipse也會處理基本知識。這很難誇大我提供的生產力收益;輕鬆獲得一個數量級的增益,特別是對於有大量測試的大型項目。

一旦你有一個IDE平方了,如果你是仍然運行到的問題是時候考慮DRY principle,其目的是確保信息只保存在一個地方(不變,屬性文件等),所以如果你以後需要改變它,波紋效應會被最小化。

0

如果你的測試是很難維持的,這是一個跡象,表明你的代碼是脆弱的。這意味着班級的定義經常發生變化。

考慮定義爲調用它使一個注入接口的類的責任。在傳遞數據,發送消息而不是操縱狀態方面考慮更多。

1

我發現一個Java的半自動化的開發人員測試工具命名爲SpryTest。它提供了一個簡單但功能強大的UI來創建隨機數據。它還支持使用powermock和easymock模擬電話。它可以生成最接近手寫測試的標準JUnit測試。它具有測試 - >源代碼同步功能。

嘗試過了並且工作非常適合我。看看在http://www.sprystone.com