2010-01-13 44 views
12

我一直在ASP.NET MVC項目工作了8個月。大多數情況下,我一直在使用TDD,但是在寫完實際的代碼之後,有些方面才被單元測試所覆蓋。總的來說,這個項目的測試覆蓋率很高。關於如何編寫重構友好的單元TDD測試技巧

我對目前的結果很滿意。重構真的非常容易,我的測試甚至在我第一次運行我的軟件之前幫助我發現了很多錯誤。另外,我開發了更復雜的假貨和幫手,以幫助我最小化測試代碼。

不過,我真的不喜歡的是事實,我經常發現自己不得不更新現有的單元測試來佔我的軟件做的重構。重構軟件現在是快速和無痛的,但重構我的單元測試是相當無聊和乏味的。實際上,維護我的單元測試的成本要高於首先編寫它們的成本。

我不知道我是否會做錯了什麼,或者如果測試開發的成本,這種關係與測試維護正常。我已經嘗試寫儘可能多的測試,以便覆蓋我的用戶故事,而不是系統地覆蓋我的對象界面,如this blog article中所建議的。

另外,你對如何編寫TDD的測試,使重構的假期,因爲一些測試儘可能任何進一步的提示嗎?

編輯:正如Henning和tvanfosson正確指出的那樣,它通常是編寫和維護成本最高的設置部分。根據我的經驗,破壞性測試通常是對域模型進行重構的結果,與這些測試的設置部分不兼容。

+1

你可以舉一個具體的例子來說明你的代碼是如何重構你的單元測試的?雖然有時會發生這種情況,但我個人並不認爲真正的「單元」測試應該受到重構的影響(除了移動一些測試和/或爲編寫代碼編寫新測試之外)。我意識到這不是一個非常有用的答案,我希望具體的例子可能會導致一個。 – kdgregory 2010-01-13 12:40:02

回答

8

這是一個衆所周知的問題,可以通過根據最佳實踐編寫測試來解決。這些做法在優秀的xUnit Test Patterns中有描述。本書描述了導致不可檢測測試的測試氣味,並提供瞭如何編寫可維護單元測試的指導。

在長期遵循這些模式之後,我寫了一個開源的庫,它封裝了很多這些核心模式。

它可以作爲Test Data Builder使用,但也可以作爲Auto-Mocking容器工作,並執行許多其他奇怪而美妙的事情。

它對維護方面有很大幫助,因爲它大大提高了編寫測試的抽象級別。測試變得更多聲明式,因爲您可以聲明您想要某個類型的實例而不是明確寫入它如何創建

試想一下,你有這個構造函數簽名

public MyClass(Foo foo, Bar bar, Sgryt sgryt) 

只要AA級爲AutoFixture可以解決所有構造函數的參數,你可以簡單地創建一個新的實例是這樣的:

var sut = fixture.CreateAnonymous<MyClass>(); 

的主要的好處是,如果您決定重構MyClass構造函數,則不會有任何測試中斷,因爲AutoFixture會爲您解決問題。

這只是AutoFixture可以做什麼的一瞥。它是一個獨立的庫,所以它可以與您的單元測試框架一起工作。

+0

非常感謝,我會看看! – 2010-01-13 13:28:22

+1

@Mark Seemann謝謝你的回答。提到xUnit測試模式書對我來說特別有用。這看起來正是我要找的東西:我一定要訂購一份。 – phuibers 2011-08-23 07:57:11

1

我認爲他的意思是,它的設置部分是非常繁瑣的維護。 我們遇到了完全相同的問題,尤其是當我們引入新的依賴關係,拆分依賴關係或以其他方式更改代碼應該如何使用時。

在大多數情況下,當我編寫和維護的單元測試,我把我的時間以書面形式設置/安排代碼。 在我們的許多測試中,我們都有完全相同的設置代碼,而且我們有時使用專用幫助程序方法來執行實際設置,但使用不同的值。

然而,這不是一個很好的事情,因爲我們還必須創建在每個測試所有這些值。因此,我們現在正在考慮以更多規範/ BDD風格編寫我們的測試,這應該有助於減少設置代碼,從而減少維護測試所花費的時間。 您可以查看的一些資源是http://elegantcode.com/2009/12/22/specifications/,以及使用MSpec進行BDD測試的樣式http://elegantcode.com/2009/07/05/mspec-take-2/

2

您可能會將單元測試編寫得離您的課程太近。你應該做的是測試公共API。當我指的是公共API時,我不是指所有類的公共方法,我的意思是你的公共控制器。

通過讓測試模仿了用戶如何將與您的控制器部分相互作用而沒有觸及你的模型類或輔助功能直接,你讓自己重構你的代碼,而無需重構你的測試。當然,有時候甚至是你的公共API改變,然後你仍然需要改變你的測試,但這種情況發生的頻率會更低。

這種方法的不足之處在於,您經常需要通過複雜的控制器設置才能測試您想要介紹的新輔助函數,但我認爲最終它是值得的。此外,您最終將以更智能的方式組織您的測試代碼,從而使設置代碼更易於編寫。

+0

你的回答是正確的,但它總結了我已經引用的博客文章... – 2010-01-13 12:58:04

+0

然後,你必須做錯了什麼,因爲不,當你重構你的代碼時,你不應該經常重構你的測試。 – 2010-01-13 13:05:20

+0

我沒有經常說。但我確實發現維護成本超過了創建tdd測試的成本。你的經歷有什麼不同? – 2010-01-13 13:12:54

2

這篇文章對我幫助很大:http://msdn.microsoft.com/en-us/magazine/cc163665.aspx

在另一方面,沒有奇蹟的方法來避免重構單元測試。

一切都伴隨着價格,尤其是如果你想做單元測試的話。

+0

感謝您的文章,我會看看。 我當然知道所有東西都是有代價的,我想知道爲什麼人們抱怨單元測試過於乏味,無法在TDD的真正意義上討論,而我花更多的時間來維護它們而不是寫它們。 – 2010-01-13 12:53:27

1

大多數情況下,我看到這樣的重構影響了單元測試的設置,通常涉及添加依賴關係或改變對這些依賴關係的期望。這些依賴關係可能會在後續功能中引入,但會影響早期的測試。在這些情況下,我發現它對重構設置代碼非常有用,以便它被多個測試共享(參數化,以便可以靈活配置)。然後,當我需要對影響設置的新功能進行更改時,我只需要在一個地方重構測試。

+1

我也通過添加假對象工廠來做類似的事情。我也有一個基礎測試類,負責依賴注入。有時我也有一個由多個測試共享的設置方法。我想這就是重構設置代碼的意思嗎? – 2010-01-13 12:57:03

0

當我開始感受到設置周圍的重構痛苦時,我關注的兩個領域是讓我的單元測試更具體,我的方法/類更小。基本上我發現我正在脫離SOLID/SRP。或者我有很多嘗試做的測試。

值得注意的是,我儘量遠離BDD/context規範,遠離我所獲得的UI。測試一個行爲是很好的,但總是會讓我(也許我沒有做對嗎?)更大的messier測試,更多的上下文規範比我喜歡。

我看到這種情況發生在我身上的另一種方式是作爲代碼借記卡,逐漸進入隨着時間推移增長其業務邏輯的方法。當然,總有大的方法和類有多個依賴關係,但是我有更少的「測試重寫」。

0

如果您發現自己創建涉及像Russian dolls這樣的深度對象圖的複雜測試腳手架,請考慮重構您的代碼,以便受測試類完全獲取其構造函數/參數中所需的內容,而不是讓它走過圖形。

的這一翻譯:

public class A { 

    public void foo(B b) { 
     String someField = b.getC().getD().getSomeField(); 
     // ... 
    } 
} 

將其更改爲:

public class A { 

    public void foo(String someField) { 
     // ... 
    } 
} 

然後測試設置變得微不足道。