2015-11-29 95 views
5

我經常使用代表工廠生產實體的類。 爲了能夠輕鬆地輕鬆測試我的工廠,我通常實施IEquatable<T>,同時覆蓋GetHashCodeEquals(如MSDN所示)。我應該使用IEquatable來簡化工廠測試嗎?

例如;以下面的實體類爲例進行簡化。通常我的課有更多的屬性。偶爾也有一個集合,它在Equals方法中使用SequenceEqual進行檢查。

public class Product : IEquatable<Product> 
{ 
    public string Name 
    { 
     get; 
     private set; 
    } 

    public Product(string name) 
    { 
     Name = name; 
    } 

    public override bool Equals(object obj) 
    { 
     if (obj == null) 
     { 
      return false; 
     } 

     Product product = obj as Product; 

     if (product == null) 
     { 
      return false; 
     } 
     else 
     { 
      return Equals(product); 
     } 
    } 

    public bool Equals(Product other) 
    { 
     return Name == other.Name; 
    } 

    public override int GetHashCode() 
    { 
     return Name.GetHashCode(); 
    } 
} 

這意味着然後我可以做簡單的單元測試像這樣(假設構造方法中的其他地方進行測試)。

[TestMethod] 
public void TestFactory() 
{ 
    Product expected = new Product("James"); 

    Product actual = ProductFactory.BuildJames(); 

    Assert.AreEqual(expected, actual); 
} 

然而這引起了許多問題。

  1. GetHashCode實際上沒有使用,但我花了時間來實現它。
  2. 我很少真的想在我的實際應用中使用Equals超出測試。
  3. 我花更多時間編寫更多測試以確保Equals實際上正常工作。
  4. 我現在有另外三種方法來維護,例如,向類中添加一個屬性,更新方法。

但是,這確實給了我一個非常整潔TestMethod

這是否適合使用IEquatable,還是我應該採取另一種方法?

+0

使用IEquatable肯定是不合適的,因爲產品不僅有一個平等的概念。但是你可以使用一個'IEqualityComparer',Resharper也可以爲你生成。是否測試這樣一個生成的比較器是一個選擇。 – usr

+1

基於可變字段的GetHashCode是一個壞主意http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx –

回答

4

這是一個好主意還是不是真的取決於你的工廠創建什麼樣的類型。有兩種類型:(引用類型的簡稱)

  • 類型與值語義(值類型的簡稱)和

  • 類型參考語義

在C#對於值類型使用struct而對於參考類型使用class是常見的,但是您不必爲此而使用class。的一點是,:

  • 值類型意味着是小的,通常不變的,自包含的對象,其主要目的是爲了含有一定值,而

  • 引用類型是具有複雜對象可變的狀態,對其他對象可能引用和非平凡的功能,即算法,業務邏輯等

如果你的工廠創建值類型,然後確定,繼續前進,使其IEquatable和使用這個ne在把戲。但是在大多數情況下,我們不使用工廠來創建價值類型,這往往是相當微不足道的,我們使用工廠作爲引用類型,因爲它們往往相當複雜,所以如果您的工廠正在創建引用類型,那麼確實如此類型的對象僅用於參考,因此添加Equals()GetHashCode()方法可能會導致誤導或錯誤。

從哈希映射會發生什麼提示:類型中Equals()GetHashCode()的存在通常表示您可以使用此類型的實例作爲哈希映射中的鍵;但如果對象不是不可變的值類型,那麼它的狀態可能會在它被放置到地圖中後發生改變,在這種情況下,方法將開始評估其他內容,但散列地圖將永遠不會再次調用GetHashCode()爲了重新定位地圖中的對象。這種情況下的結果往往是混亂的。

所以,底線是,如果你的工廠創建複雜的對象,那麼你應該採取不同的方法。顯而易見的解決方案是調用工廠,然後檢查返回對象的每個屬性以確保它們完全符合預期。

我或許可以對此進行改進,但要小心,我只是想到了這一點,我從未嘗試過,所以在實踐中它可能並不一定是個好主意。這是:

您的工廠可能會創建實現特定接口的對象。 (否則,擁有一個工廠有什麼意義?)所以,理論上你可以規定新創建的實現這個接口的對象的實例應該具有初始化爲特定值的某些屬性。這將是一個由接口強加的規則,所以你可以有一些與接口相關的函數來檢查這是否爲真,並且這個函數甚至可以用一些提示進行參數化,以便在不同情況下期望不同的初始值。

(上次我檢查,在C#捆綁接口的方法通常是一個擴展方法;我不記得了我的頭頂,C#是否允許靜態方法是一個接口的一部分,還是。C#的設計者還沒有添加到語言的東西整齊而優雅如Java的默認界面方法)

所以,用一個擴展方法,它也許可以是這樣的:

public boolean IsProperlyInitializedInstance(this IProduct self, String hint) 
{ 
    if(self.Name != hint) 
     return false; 
    //more checks here 
    return true; 
} 

IProduct product = productFactory.BuildJames(); 
Assert.IsTrue(product.IsProperlyInitializedInstance(hint:"James")); 
1

對於測試代碼,您可以使用反射例如:Comparing object properties in c#

許多測試庫都提供了這樣的實用程序,這樣您就不必更改生產代碼的設計以適應測試。

相關問題