2011-08-16 36 views
3

我已經閱讀過幾本有關TDD和BDD的書籍和文章,應該避免在單個單元測試或規範中出現多重斷言或期望。我可以理解這樣做的原因。但我不確定什麼是驗證複雜結果的好方法。多重期望/斷言來驗證測試結果

假設被測方法返回一個複雜的對象(例如反序列化或數據庫讀取),如何正確驗證結果?

1.Asserting每個屬性:

Assert.AreEqual(result.Property1, 1); 
    Assert.AreEqual(result.Property2, "2"); 
    Assert.AreEqual(result.Property3, null); 
    Assert.AreEqual(result.Property4, 4.0); 

2.Relying上的正確實施.Equals():

Assert.AreEqual(result, expectedResult); 

1.缺點是,如果第一斷言失敗所有以下聲明不運行,這可能包含有價值的信息來找到問題。隨着房產的進出,可維護性也可能成爲問題。

2.的缺點是我似乎在用這個測試測試多個東西。如果.Equals()沒有正確實現,我可能會出現誤報或負面情況。我也沒有看到,如果測試失敗,實際上哪些屬性會有所不同,但我認爲這通常可以通過體面的.ToString()覆蓋來解決。無論如何,我認爲我應該避免被迫在失敗的測試中拋出調試器來查看差異。我應該馬上看到它。

下一個問題2是它比較,即使對於一些測試只一些性質可能是顯著的整個對象。

對於TDD和BDD來說,這將是一種體面的方式或最佳做法。

回答

0

與存在於問題的情況下我會去選擇1

這可能取決於上下文。如果我在.NET框架中使用某種內置的對象序列化,我可以合理地確信,如果沒有遇到錯誤,那麼整個對象就會被恰當地編組。在這種情況下,在對象中聲明單個字段可能沒問題。我相信MS庫做正確的事情。

如果您正在使用SQL和手動映射結果的域對象,我覺得選擇1使它更快地診斷,當東西壞了不是選項2選項2可能爲了使斷言失敗依靠toString方法:

 
Expected <1 2 null 4.0> but was <1 2 null null> 

現在我陷入困境試圖找出什麼字段4.0/null是。當然,我可以把字段名進入方法:

 
Expected <Property1: 1, Property2: 2, Property3: null, Property4: 4.0> 
but was <Property1: 1, Property2: 2, Property3: null, Property4: null> 

這是罰款性質的小數目,但開始以破壞較大,由於包裹性,等等。同樣的數字,toString維護可能成爲因爲它需要以與equals方法相同的速率進行更改。

當然,沒有正確的答案,在一天結束時,它真的歸結爲您的團隊(或您自己)的個人偏好。

希望有幫助!

布蘭登

0

我會默認使用第二種方法。你是對的,如果Equals()沒有正確實現,這會失敗,但如果你已經實現了自定義Equals(),那麼你也應該進行單元測試。

第二種方法實際上更加抽象和簡潔,並允許您稍後更容易地修改代碼,從而以相同的方式減少代碼重複。比方說,你選擇第一種方法:

  • 你必須對所有屬性在幾個地方比較,
  • 如果添加一個新的屬性的一類,你必須在單位添加一個新的斷言測試;修改你的Equals()會容易得多。當然,您還需要在預期結果中添加屬性的值(如果它不是默認值),但是添加新斷言的時間會更短。

此外,它更容易看到哪些屬性實際上與第二種方法不同。您只需在調試模式下運行測試,並比較中斷時的屬性。

順便說一句,你不應該使用ToString()這個。我想你想說[DebuggerDisplay]屬性?

下一個問題2。是它比較了整個對象,即使對於一些測試,只有一些屬性可能是重要的。

如果僅比較某些屬性,比:

  • 醚你通過實現一個基類,它僅包含那些屬性重構你的代碼。示例:如果要將Cat與另一個Cat進行比較,但僅考慮Dog和其他動物的常見屬性,請執行Cat : Animal並比較基類。
  • 或者你做你在第一種方法中所做的。例如:如果你只關心預期的和實際的貓喝完的牛奶數量以及它們各自的名字,你的單元測試中會有兩個斷言。
+0

我傾向於不應該拋出調試器在失敗的測試看到的差異。另外DebuggerDisplay不會顯示在測試報告中,我想。 – bitbonk

2

不要從字面上理解TDD的建議。 「好人」的意思是你應該在每個測試中測試一件事情(爲了避免測試因爲多種原因而失敗,並且隨後必須調試測試以找出原因)。 現在測試「一件事」是指「一種行爲」;沒有一個斷言每個測試恕我直言。

這不是一條規則。

所以選擇: 爲了比較整個數據值對象

  • 如果該對象已公開可用的生產平等相待,使用它。
  • 否則不會添加僅用於測試的等於(See also Equality Pollution)。使用助手/擴展方法(或者你可以在斷言庫中找到一個)obj1。HasSamePropertiesAs(OBJ2)

爲了比較對象的非結構化部分(任意屬性的集合 - 這應該是罕見的),

  • 創造一個良好命名的私有方法AssertCustomerDetailsInOrderEquals(PARAMS),這樣的測試是明確的在你實際測試的部分。將這組斷言移動到私有方法中。
0

嘗試「每個測試的行爲的一個方面」而不是「每個測試的一個斷言」。如果您需要多個斷言來說明您感興趣的行爲,請執行此操作。

例如,您的示例可能是ShouldHaveSensibleDefaults。將其拆分爲ShouldHaveADefaultNameAsEmptyString,ShouldHaveNullAddress,ShouldHaveAQuantityOfZero等將不會清晰地讀取。它也不會幫助隱藏另一個對象的合理默認值,然後進行比較。

然而,我會單獨例子,其中值必須與來自一些邏輯衍生某處的任何屬性的默認值,例如,ShouldCalculateTheTotalQuantity。將像這樣的小例子移入他們自己的方法使其更具可讀性。

您可能還會發現,對象上的不同屬性會因不同的上下文而發生更改。分別調用這些上下文並分別查看這些屬性有助於我瞭解上下文與結果之間的關係。

Dave Astels提出了「每個測試的一個斷言」,現在也使用短語"one aspect of behavior",儘管他仍然認爲它對separate that behavior有用。我傾向於在可讀性和可維護性方面犯錯,所以如果它具有不只一個斷言的實用意義,我會這樣做。