C# 单元测试-当您的代码几乎只是一个计算(例如GetHashCode)时,您会怎么做?
当您转到GetHashCode的单元测试时,我在计算原始组件和重复函数或使用预定值之间左右为难:C# 单元测试-当您的代码几乎只是一个计算(例如GetHashCode)时,您会怎么做?,c#,unit-testing,assert,gethashcode,C#,Unit Testing,Assert,Gethashcode,当您转到GetHashCode的单元测试时,我在计算原始组件和重复函数或使用预定值之间左右为难: 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 +
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
计算本身是逻辑吗?不太可能,尽管意见可能会有所不同
这里的相关逻辑是,两个相等的对象具有相同的哈希代码,即Equals
和HashCode
是兼容的。或引用:
- 如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较结果不相等,则两个对象的GetHashCode方法不必返回不同的值
- 对象的GetHashCode方法必须始终返回相同的哈希代码,只要不修改确定对象的Equals方法返回值的对象状态。请注意,这仅适用于应用程序的当前执行,如果应用程序再次运行,则可以返回不同的哈希代码
GetHashCode
的内部实现与预定过程匹配
您的两个示例测试都非常脆弱,完全有效的
GetHashCode
实现将使它们失败。回想一下,单元测试的一个主要目的是让重构无需担心。但是,没有人能够在不破坏单元测试的情况下重构GetHashCode
。我将结果与预先计算的值进行比较,并给出大量注释,以解释我为什么选择要测试的值
对于纯计算,我倾向于编写单元测试,以确保函数保持使用它的代码所需/预期的条件
示例条件:
- 对生成的哈希值有任何约束吗?最小值、最大值、奇数、偶数等
- 它是否正确地散列正数和负数
- 它是否在适当的条件下创建唯一的值
GetHashCode
。如果你在BCL类型上使用它,你会射中自己的脚。哈希代码不等同于对象标识符。@domenic:问题是,通过重写GetHashCode,OP可能会破坏该约定;他们可能有明确的需要这样做,即使这是明确禁止的。如果没有,为什么默认行为还不够?@Paul:你的建议违反了Liskov替代原则。而且,默认行为通常是不够的,因为您有自定义的相等比较语义,并且需要更改GetHashCode
,以符合我在回答中引用的常规契约。完全同意。我同意测试散列算法的有效性(散列唯一性)非常重要,尽管我认为这可能超出了OP的问题范围。问题以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);
}