NUnit的Is.Equal对于从generics类派生的类不能可靠地工作吗?

今天我遇到了NUnit的以下问题。

我有一个类,派生自generics类。 我开始做一些序列化测试,并使用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 { } public class Client { 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)) { return false; } return Equals((Client)obj); } public bool Equals(Client 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之后,ObjectsAreEqual(对象x,对象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()(当测试失败时,前者不会给我这么好的输出)。

提前致谢。

更新:

与此同时,我进一步解决了这个问题,发现了类似的问题,并发布了一个可能的解决方法 。

问题是第二个测试的第二个断言调用Equals重载接受一个object而不是Client ,因此这个比较返回false:

 // obj.GetType() returns Client.DerrivedClient if (obj.GetType() != typeof(Client)) { return false; } 

要解决此问题,您可以将比较操作更改为:

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