public class Foo
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public override int GetHashCode()
{
var hash = 17;
hash *= 23 + x.GetHashCode();
hash *= 23 + y.GetHashCode();
hash *= 23 + z.GetHashCode();
}
}
当你去单元测试GetHashCode时,我在计算原始组件和重复函数或使用预定值之间徘徊:
[TestMethod]
public void Test1
{
var x = 1; y = 2; z = 3;
var foo = new Foo() { X = x, Y = y, Z = z };
var hash = 17;
hash *= 23 + x.GetHashCode();
hash *= 23 + y.GetHashCode();
hash *= 23 + z.GetHashCode();
var expected = hash;
var actual = foo.GetHashCode();
Assert.AreEqual(expected, actual);
}
[TestMethod]
public void Test2
{
var x = 1; y = 2; z = 3;
var foo = new Foo() { X = x, Y = y, Z = z };
var expected = ? //Some predetermined value (calculated by hand?)
var actual = foo.GetHashCode();
Assert.AreEqual(expected, actual);
}
或者还有其他方法吗?
答案 0 :(得分:5)
单元测试用于测试逻辑。 GetHashCode
计算本身是逻辑吗?不是真的,虽然意见可能会有所不同。
这里的相关逻辑是两个相等的对象具有相同的哈希码,即Equals
和HashCode
是兼容的。或者,引用the docs:
如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。
对象的GetHashCode方法必须始终返回相同的哈希码,只要对对象状态没有修改即可确定对象的Equals方法的返回值。请注意,这仅适用于当前应用程序的执行,并且如果再次运行应用程序,则可以返回不同的哈希代码。
所以我会编写单元测试来保证满足这些条件,而不是GetHashCode
的内部实现符合预定的过程。
您的两个示例测试都非常脆弱,完全有效的GetHashCode
实现会使它们失败。回想一下,单元测试的主要目的之一是允许重构而不用担心。但是,如果没有单位测试,没有人可以重构GetHashCode
。
答案 1 :(得分:1)
我将结果与预先计算的值进行比较,并通过大量注释来解释为什么我选择了被测试的值。
对于纯计算,我倾向于编写单元测试,以便它们确保函数保持将使用它的代码所需/期望的条件。
示例条件: