【问题标题】:C#: How would you unit test GetHashCode?C#:您将如何对 GetHashCode 进行单元测试?
【发布时间】:2010-12-14 09:21:41
【问题描述】:

测试Equals 方法非常简单(据我所知)。但是到底如何测试GetHashCode 方法呢?

【问题讨论】:

    标签: c# unit-testing tdd gethashcode


    【解决方案1】:

    测试两个不同的相等的对象是否具有相同的哈希码(对于不同的值)。检查不相等的对象是否给出不同的哈希码,一次改变一个方面/属性。虽然哈希码没有 不同,但如果您选择不同的属性值, 会给出相同的哈希码,否则您真的很不走运,除非您已经有错误。

    【讨论】:

    • 仅仅因为其他人的代码未能产生分布良好的哈希值并不意味着它不是对您的代码的良好测试。
    • @Tony:那你平时用什么?
    • @Svish:我不记得名字了,但是重复了乘法和加法 - 找到我关于 GetHashCode 的答案,我相信你会看到很多例子 :)
    • 据我所知,单元测试的目的是验证方法是否按文档说明工作。 GetHashCode 的唯一要求是:If two objects compare as equal, the GetHashCode method for each object must return the same value. 为不同的对象返回不同的值是性能问题:GetHashCode 总是返回 0 是一个严重的性能缺陷,但实际上它仍然一个有效 哈希函数。这应该是某种代码审查的主题,而不是单元测试。我说的对吗?
    • 我想说的是,只有一个测试检查,如果 GetHashCode 为相等的对象返回相同的值,则应该实现 GetHashCode 被覆盖并由用户实现。
    【解决方案2】:

    Gallio/MbUnit v3.2 带有方便的合约验证器,能够测试您对GetHashCode()IEquatable<T> 的实现。更具体地说,您可能对EqualityContractHashCodeAcceptanceContract 感兴趣。有关详细信息,请参阅hereherethere

    public class Spot
    {
      private readonly int x;
      private readonly int y;
    
      public Spot(int x, int y)
      {
        this.x = x;
        this.y = y;
      }
    
      public override int GetHashCode()
      {
        int h = -2128831035;
        h = (h * 16777619) ^ x;
        h = (h * 16777619) ^ y;
        return h;
      }
    }
    

    然后你像这样声明你的合约验证者:

    [TestFixture]
    public class SpotTest
    {
      [VerifyContract]
      public readonly IContract HashCodeAcceptanceTests = new HashCodeAcceptanceContract<Spot>()
      {
        CollisionProbabilityLimit = CollisionProbability.VeryLow,
        UniformDistributionQuality = UniformDistributionQuality.Excellent,
        DistinctInstances = DataGenerators.Join(Enumerable.Range(0, 1000), Enumerable.Range(0, 1000)).Select(o => new Spot(o.First, o.Second))
      };
    }
    

    【讨论】:

      【解决方案3】:

      它与 Equals() 非常相似。您需要确保两个“相同”的对象至少具有相同的哈希码。这意味着如果 .Equals() 返回 true,则哈希码也应该相同。至于正确的哈希码值是什么,这取决于您的哈希方式。

      【讨论】:

      • +1 - 这绝对是要测试的一件事。忘记分发,但相同的对象必须具有相同的哈希码。
      【解决方案4】:

      根据个人经验。除了像相同的对象给您相同的哈希码这样显而易见的事情之外,您还需要创建足够大的唯一对象数组并计算其中的唯一哈希码。如果唯一的哈希码少于总对象数的 50%,那么您就有麻烦了,因为您的哈希函数不好。

              List<int> hashList = new List<int>(testObjectList.Count);
              for (int i = 0; i < testObjectList.Count; i++)
              {
                  hashList.Add(testObjectList[i]);
              }
      
              hashList.Sort();
              int differentValues = 0;
              int curValue = hashList[0];
              for (int i = 1; i < hashList.Count; i++)
              {
                  if (hashList[i] != curValue)
                  {
                      differentValues++;
                      curValue = hashList[i];
                  }
              }
      
              Assert.Greater(differentValues, hashList.Count/2);
      

      【讨论】:

        【解决方案5】:

        我会预先提供一个已知/预期的哈希值并比较 GetHashCode 的结果。

        【讨论】:

        • 这使得测试非常脆弱。例如,您应该能够使 GetHashCode 返回它在以前版本中给出的否定值,并且该方法仍然有效。测试您关心的内容 - 比较相等和不相等值的哈希码。
        【解决方案6】:

        您创建具有相同值的单独实例,并检查实例的 GetHashCode 是否返回相同的值,以及对同一实例的重复调用是否返回相同的值。

        这是哈希码工作的唯一要求。要想运行良好,哈希码当然应该有一个良好的分布,但是测试需要大量的测试......

        【讨论】:

          【解决方案7】:

          除了检查对象相等是否意味着哈希码相等,并且按照 Yann Trevin 的建议,哈希的分布相当平坦(如果性能是一个问题),您可能还希望考虑如果更改属性会发生什么对象。

          假设您的对象在字典/哈希集中发生变化。您是否希望 Contains(object) 仍然为真?如果是这样,那么您的 GetHashCode 最好不要依赖于已更改的可变属性。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-29
            • 1970-01-01
            相关资源
            最近更新 更多