C#: Как бы вы протестировали модуль GetHashCode?
Тестирование Equals
метод довольно прямой (насколько я знаю). Но как же ты проверяешь GetHashCode
метод?
7 ответов
Проверьте, что два разных равных объекта имеют одинаковый хэш-код (для различных значений). Убедитесь, что неравные объекты дают разные хэш-коды, изменяя один аспект / свойство за раз. Хотя хеш-коды не должны быть разными, вам действительно не повезет выбирать разные значения для свойств, которые дают одинаковый хеш-код, если у вас нет ошибки.
Gallio/MbUnit v3.2 поставляется с удобными верификаторами контрактов, которые могут проверить вашу реализацию GetHashCode()
а также IEquatable<T>
, Более конкретно вы можете быть заинтересованы EqualityContract
и HashCodeAcceptanceContract
, Смотрите здесь, здесь и там для более подробной информации.
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))
};
}
Это было бы довольно похоже на Equals(). Вы хотели бы убедиться, что два объекта, которые были "одинаковыми", по крайней мере, имели одинаковый хэш-код. Это означает, что если.Equals() возвращает true, хеш-коды также должны быть идентичны. Что касается правильных значений хеш-кода, это зависит от того, как вы хэшируете.
Из личного опыта. Помимо очевидных вещей, таких как одни и те же объекты, дающие вам одинаковые хеш-коды, вам необходимо создать достаточно большой массив уникальных объектов и подсчитать среди них уникальные хеш-коды. Если уникальные хеш-коды составляют менее, скажем, 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);
В дополнение к проверке того, что равенство объектов подразумевает равенство хеш-кодов, и распределение хешей является довольно плоским, как это было предложено Янном Тревином (если производительность вызывает сомнения), вы также можете рассмотреть, что произойдет, если вы измените свойство объекта.
Предположим, ваш объект изменяется, пока он находится в словаре / хэш-наборе. Хотите ли вы, чтобы объект Contains(объект) оставался истинным? Если это так, то ваш GetHashCode не должен зависеть от изменяемого свойства.
Я бы предварительно поставил известный / ожидаемый хеш и сравнил бы результат GetHashCode.
Вы создаете отдельные экземпляры с одинаковым значением и проверяете, что GetHashCode для экземпляров возвращает одно и то же значение, а повторные вызовы для одного экземпляра возвращают одно и то же значение.
Это единственное требование для работы хеш-кода. Для правильной работы хэш-коды должны, конечно, иметь хорошее распространение, но тестирование для этого требует большого тестирования...