2009-10-26 26 views
8

今天我遇到了NUnit的下列問題。NUnit的Is.EqualTo對從泛型類派生的類無法可靠地工作嗎?

我有一個類,它來自一個泛型類。 我開始做一些序列化測試,並使用NUnit的Is.EqualTo()函數測試相等性。

我開始懷疑某件事情是錯誤的,當一個本應該失敗的測試通過時。當我使用obj1.Equals(obj2)時,它失敗了,因爲它應該。

探討我創建了以下測試:

namespace NUnit.Tests 

{ 

using Framework; 

    public class ThatNUnit 
    { 
     [Test] 
     public void IsNotEqualTo_ClientsNotEqual_Passes() 
     { 
      var client1 = new DerrivedClient(); 
      var client2 = new DerrivedClient(); 

      client1.Name = "player1"; 
      client1.SomeGenericProperty = client1.Name; 
      client2.Name = "player2"; 
      client2.SomeGenericProperty = client2.Name; 

      Assert.That(client1.Equals(client2), Is.False); 
      Assert.That(client1, Is.Not.EqualTo(client2)); 
     } 

     [Test] 
     public void IsNotEqualTo_ClientsAreEqual_AlsoPasses_SomethingWrongHere() 
     { 
      var client1 = new DerrivedClient(); 
      var client2 = new DerrivedClient(); 

      client1.Name = "player1"; 
      client1.SomeGenericProperty = client1.Name; 
      client2.Name = client1.Name; 
      client2.SomeGenericProperty = client1.Name; 

      Assert.That(client1.Equals(client2), Is.True); 
      Assert.That(client1, Is.Not.EqualTo(client2)); 
     } 
    } 

    public class DerrivedClient : Client<string> 
    { 
    } 

    public class Client<T> 
    { 
     public string Name { get; set; } 

     public T SomeGenericProperty { get; set; } 

     public override bool Equals(object obj) 
     { 
      if (ReferenceEquals(null, obj)) 
      { 
       return false; 
      } 
      if (ReferenceEquals(this, obj)) 
      { 
       return true; 
      } 
      if (obj.GetType() != typeof(Client<T>)) 
      { 
       return false; 
      } 
      return Equals((Client<T>)obj); 
     } 

     public bool Equals(Client<T> other) 
     { 
      if (ReferenceEquals(null, other)) 
      { 
       return false; 
      } 
      if (ReferenceEquals(this, other)) 
      { 
       return true; 
      } 
      return Equals(other.Name, Name) && Equals(other.SomeGenericProperty, SomeGenericProperty); 
     } 

     public override int GetHashCode() 
     { 
      unchecked 
      { 
       return ((Name != null ? Name.GetHashCode() : 0) * 397)^SomeGenericProperty.GetHashCode(); 
      } 
     } 

     public override string ToString() 
     { 
      return string.Format("{0}, {1}", Name, SomeGenericProperty); 
     } 
    } 
} 

兩個(實際上是相互矛盾的斷言)在第二次測試顯示問題:

Assert.That(client1.Equals(client2), Is.True); 
Assert.That(client1, Is.Not.EqualTo(client2)); 

這個測試應該會失敗的一種方式或其他,但它不!

所以我挖了一點NUnit的源代碼,只是發現,在一些if()s後的一些特殊條件下,ObjectAreEqual(object x,object y)方法(最終通過Assert.That調用(X,Is.EqualTo(Y)),談到這行代碼:

return x.Equals(y); 

我覺得很困惑,因爲我現在必須考慮,即Is.EqualTo()只需要一個較長的路線,但基本上應該與x.Equals(y)

一樣。這裏有關於誰有興趣的全部方法(在NUNit.Framework.Constraints名稱空間內):

public bool ObjectsEqual(object x, object y) 
    { 
     this.failurePoints = new ArrayList(); 

     if (x == null && y == null) 
      return true; 

     if (x == null || y == null) 
      return false; 

     Type xType = x.GetType(); 
     Type yType = y.GetType(); 

     if (xType.IsArray && yType.IsArray && !compareAsCollection) 
      return ArraysEqual((Array)x, (Array)y); 

     if (x is ICollection && y is ICollection) 
      return CollectionsEqual((ICollection)x, (ICollection)y); 

     if (x is IEnumerable && y is IEnumerable && !(x is string && y is string)) 
      return EnumerablesEqual((IEnumerable)x, (IEnumerable)y); 

     if (externalComparer != null) 
      return externalComparer.ObjectsEqual(x, y); 

     if (x is string && y is string) 
      return StringsEqual((string)x, (string)y); 

     if (x is Stream && y is Stream) 
      return StreamsEqual((Stream)x, (Stream)y); 

     if (x is DirectoryInfo && y is DirectoryInfo) 
      return DirectoriesEqual((DirectoryInfo)x, (DirectoryInfo)y); 

     if (Numerics.IsNumericType(x) && Numerics.IsNumericType(y)) 
      return Numerics.AreEqual(x, y, ref tolerance); 

     if (tolerance != null && tolerance.Value is TimeSpan) 
     { 
      TimeSpan amount = (TimeSpan)tolerance.Value; 

      if (x is DateTime && y is DateTime) 
       return ((DateTime)x - (DateTime)y).Duration() <= amount; 

      if (x is TimeSpan && y is TimeSpan) 
       return ((TimeSpan)x - (TimeSpan)y).Duration() <= amount; 
     } 

     return x.Equals(y); 
    } 

那麼這裏發生了什麼,它如何被修復?

我想能夠相信我的測試,因此必然再次NUnit。

我也不想開始使用Equals()而不是Is.EqualTo()(當測試失敗時,前者不會給我這麼好的輸出)。

在此先感謝。

更新:

在我這個問題進一步搏鬥,發現了類似的問題here並張貼了可能workaround其間。

回答

5

的問題是,第二次測試的第二個斷言調用Equals重載接受一個object而非Client<T>,所以這個比較返回false:

// obj.GetType() returns Client.DerrivedClient 

if (obj.GetType() != typeof(Client<T>)) 
{ 
    return false; 
} 

爲了解決這個問題,你可以改變的比較操作到這:

if (obj.GetType() != this.GetType()) 
+0

謝謝傑夫,這似乎是在正確的軌道上。 我的簡單例子得到了固定的方式,但對於真實的情況,我還在掙扎。 這將教會我在未來不僅僅將生成的代碼的有效性視爲理所當然。 – 2009-10-26 15:35:09

+0

我的榮幸 - 我想象真實的情況是一種嚴重的痛苦;我唯一的建議是休息一下(即使是短暫的!),這樣你可以用新鮮的眼睛看看它。祝你好運! – 2009-10-26 16:14:45