Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单元测试-当您的代码几乎只是一个计算(例如GetHashCode)时,您会怎么做?_C#_Unit Testing_Assert_Gethashcode - Fatal编程技术网

C# 单元测试-当您的代码几乎只是一个计算(例如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 +

当您转到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 + x.GetHashCode();
        hash *= 23 + y.GetHashCode();
        hash *= 23 + z.GetHashCode();
    }
}

或者有其他方法吗?

单元测试用于测试逻辑。
GetHashCode
计算本身是逻辑吗?不太可能,尽管意见可能会有所不同

这里的相关逻辑是,两个相等的对象具有相同的哈希代码,即
Equals
HashCode
是兼容的。或引用:

  • 如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较结果不相等,则两个对象的GetHashCode方法不必返回不同的值

  • 对象的GetHashCode方法必须始终返回相同的哈希代码,只要不修改确定对象的Equals方法返回值的对象状态。请注意,这仅适用于应用程序的当前执行,如果应用程序再次运行,则可以返回不同的哈希代码

因此,我将编写单元测试,以确保满足这些条件,而不是确保
GetHashCode
的内部实现与预定过程匹配


您的两个示例测试都非常脆弱,完全有效的
GetHashCode
实现将使它们失败。回想一下,单元测试的一个主要目的是让重构无需担心。但是,没有人能够在不破坏单元测试的情况下重构
GetHashCode

我将结果与预先计算的值进行比较,并给出大量注释,以解释我为什么选择要测试的值

对于纯计算,我倾向于编写单元测试,以确保函数保持使用它的代码所需/预期的条件

示例条件:

  • 对生成的哈希值有任何约束吗?最小值、最大值、奇数、偶数等
  • 它是否正确地散列正数和负数
  • 它是否在适当的条件下创建唯一的值

Ah,所以只需测试两个相等的对象是否返回相同的哈希代码(根据实现GetHashCode的规则)。我强烈反对。GetHashCode确实是逻辑;即使它是如何实现的看起来很简单,但仍然是值得进行有效单元测试的逻辑。特别地,考虑一个对象的散列被持久化的情况,或者作为对一些持久性技术(Hibernate)的测试;标识等价性不足以确认不可变性。@Paul:文档明确指出,您不应出于这些目的使用
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);
}