为HashCode方法创建单元测试

时间:2016-06-28 17:11:46

标签: c# xunit

我有以下HashCode助手:

public struct HashCode {

  private readonly Int32 Value;

  private HashCode(Int32 value) {
    Value = value;
  }

  public static implicit operator Int32(HashCode hashCode) {
    return hashCode.Value;
  }

  public static HashCode Of<T>(T item) {
    return new HashCode(GetHashCode(item));
  }

  public HashCode And<T>(T item) {
    return new HashCode(CombineHashCodes(Value, GetHashCode(item)));
  }

  public HashCode AndEach<T>(IEnumerable<T> items) {      
    Int32 hashCode = items.Select(x => GetHashCode(x)).Aggregate((x, y) => CombineHashCodes(x, y));
    return new HashCode(CombineHashCodes(Value, hashCode));
  }

  private static Int32 CombineHashCodes(Int32 x, Int32 y) {

    // Taken from:
    https://github.com/dotnet/coreclr/blob/775003a4c72f0acc37eab84628fcef541533ba4e/src/mscorlib/src/System/Tuple.cs#L56

    unchecked {        
      return ((x << 5) + x) ^ y;
    }
  }

  private static Int32 GetHashCode<T>(T item) {
    return item == null ? 0 : item.GetHashCode();
  }

}

我按如下方式使用它:

HashCode.Of(value1).And(value2).AndEach(collection);

我应该如何创建单元测试来测试这个HashCode?

2 个答案:

答案 0 :(得分:1)

创建一个或两个具有硬编码HashCode的类,然后根据您想要的粒度单元测试的数量创建一个使用您的帮助器计算哈希码的测试/测试:

var result = HashCode
  .Of(new HardcodedHashCode(5))
  .And(new HardcodedHashCode(1));

Assert.Equals(result, manually_computed_value);

对于HashCode助手的每次使用,您必须手动计算预期的哈希码 。我建议对OfAndAndEach进行一次测试,再加上使用所有测试的单一测试。

编辑更多代码:

public class HardcodedHashCode {
  private readonly int _hashCode;

    public HardcodedHashCode(int hashCode) { _hashCode = hashCode; }

    public override int GetHashCode() => _hashCode; 
}

// example test
public void and_combines_hashcodes_using_xyz_method() {
   var h1 = new HardcodedHashCode(1);
   var h5 = new HardcodedHashCode(5);

   int combinedHashcode = HashCode.of(h1).And(h5);

   // sorry but can't force myself to compute manually in the evening
   Assert.Equals(_manually_compute_value_here_, combinedHashcode);
}

答案 1 :(得分:0)

我应警告您,您可能尝试使用GetHashCode方法有点不正确。您正在将.NET哈希与您自己的哈希混合使用。我想你的散列与.NET散列的目标不同。

首先,我应该提到每个.NET对象都有自己的哈希代码,并且在创建对象后不应该更改。

object[] data = new object[] { 1, new Random(), 5 }; int hash1 = HashCode.AndEach(data); data[2] = "123"; int hash2 = HashCode.AndEach(data);

我假设hash1!= hash2。对于.NET hashing it is fail

这就是为什么你的HashCode.GetHashCode实现可以与任何延迟执行(例如Linq)结合产生非常有趣的结果。

此外还有Microsoft documentation,其中包含:

  

哈希代码用于在基于哈希表的集合中进行有效插入和查找。 哈希码不是永久值

     

...

     
      
  • 不要序列化哈希码值或将它们存储在数据库中。
  •   

例如,.NET CLR 2.0和.NET CLR 4.5中Object.GetHashCode的实现有所不同。这就是为什么如果在测试中添加使用Object.GetHashCode方法的任何对象,则.NET 2.0和.NET 4.5中的单元测试结果会有所不同。

  

如果覆盖GetHashCode方法,则还应该重写Equals,反之亦然。

因此csharpfolk提供的代码对于类HardcodedHashCode来说并不完全正确。如果两个对象相等,则它们的哈希码必须相等(它来自哈希定义)。 HardcodedHashCode应该重写Equals方法,或者有人可以创建单元测试,这对.NET散列是错误的。

因此,我建议您从代码中删除Object.GetHashCode的任何来电。或者确保您的代码不违反Microsoft的指南。