【问题标题】:Unit Testing - What do you do when your code is pretty much just a calculation (GetHashCode for example)? [duplicate]单元测试 - 当您的代码几乎只是一个计算(例如 GetHashCode)时,您会怎么做? [复制]
【发布时间】:2011-07-13 18:52:30
【问题描述】:
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);
}

或者有其他方法吗?

【问题讨论】:

    标签: c# unit-testing assert gethashcode


    【解决方案1】:

    单元测试用于测试逻辑。 GetHashCode 计算本身是否符合逻辑?并非如此,尽管意见可能会有所不同。

    这里的相关逻辑是两个相等的对象具有相同的哈希码,即EqualsHashCode是兼容的。或者,引用the docs

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

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

    所以我会编写单元测试来保证满足这些条件,而不是保证GetHashCode 的内部实现与预先确定的过程相匹配。

    您的两个示例测试都非常脆弱,完全有效的GetHashCode 实现会使它们失败。回想一下,单元测试的主要目的之一是允许重构而不用担心。但是,没有人可以在不破坏单元测试的情况下重构 GetHashCode

    【讨论】:

    • 啊,所以只需测试 2 个 equal 对象返回相同的哈希码(根据实现 GetHashCode 的规则)。
    • 我强烈反对。 GetHashCode 确实是逻辑;即使它如何到达那里看起来很简单,它仍然是值得有效单元测试的逻辑。特别是,考虑对象的哈希被持久化的情况,或者作为对某种持久性技术的测试(想想 Hibernate);身份等效性不足以确认不可变性。
    • @Paul:文档特别说您不应该将GetHashCode 用于这些目的。如果您将它用于 BCL 类型的这些目的,您将自取其辱。哈希码不等同于对象标识符。
    • @domenic:关键是通过覆盖 GetHashCode,OP 可能会破坏该合同;他们可能有明显的需要这样做,即使它被明确禁止。如果没有,为什么默认行为还不够?
    • @Paul:你的建议违反了里氏替换原则。而且,默认行为通常是不够的,因为您具有自定义的相等比较语义,并且您需要更改 GetHashCode 以符合我在回答中引用的通常合同。
    【解决方案2】:

    我将结果与预先计算的值进行比较,并附有大量评论,以解释我为什么选择正在测试的值。

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

    示例条件:

    • 对生成的哈希值是否有任何限制?最小值、最大值、奇数、偶数等。
    • 它是否正确散列正数和负数?
    • 它是否在适当的条件下创造了独特的价值?

    【讨论】:

    • 完全同意。我同意针对哈希算法的有效性(哈希唯一性)进行测试非常重要,尽管我认为这可能超出了 OP 问题的范围。
    • 问题以GetHashCode为例,我的主要观点是应该测试函数的合约。 “它是否在适当的条件下创造了独特的价值?”只是传达这一点的另一个例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-13
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多