C#: ¿Cómo probarías la unidad GetHashCode?
unit-testing tdd (7)
Además de verificar que la igualdad de objeto implica igualdad de hashcodes, y la distribución de hashes es bastante plana como sugiere Yann Trevin (si el rendimiento es una preocupación), también puede considerar lo que sucede si cambia una propiedad del objeto.
Supongamos que su objeto cambia mientras está en un diccionario / hashset. ¿Quieres que los Contiene (objeto) sigan siendo verdaderos? Si es así, es mejor que su GetHashCode no dependa de la propiedad mutable que se modificó.
Probar el método Equals
es bastante sencillo (hasta donde sé). ¿Pero cómo prueba el método GetHashCode
?
Crea instancias separadas con el mismo valor y comprueba que GetHashCode para las instancias devuelve el mismo valor y que las llamadas repetidas en la misma instancia devuelven el mismo valor.
Ese es el único requisito para que funcione un código hash. Para que funcione bien, los códigos hash deben tener una buena distribución, pero para eso se requieren muchas pruebas ...
De la experiencia personal. Aparte de cosas obvias como los mismos objetos que le dan los mismos códigos hash, necesita crear una matriz suficientemente grande de objetos únicos y contar códigos hash únicos entre ellos. Si los códigos hash únicos son menores que, digamos el 50% del recuento total de objetos, entonces tiene problemas, ya que su función hash no es buena.
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);
Pre-suministraría un hash conocido / esperado y compararía el resultado de GetHashCode.
Pruebe que dos objetos distintos que son iguales tienen el mismo código hash (para varios valores). Compruebe que los objetos no iguales den diferentes códigos hash, variando un aspecto / propiedad a la vez. Si bien los códigos hash no tienen que ser diferentes, sería muy desafortunado elegir valores diferentes para las propiedades que dan el mismo código hash a menos que tenga un error.
Sería bastante similar a Equals (). Debería asegurarse de que dos objetos que eran "iguales" al menos tuvieran el mismo código hash. Eso significa que si .Equals () devuelve verdadero, los códigos hash también deberían ser idénticos. En cuanto a cuáles son los valores de hashcode correctos, eso depende de cómo has hashing.
Gallio / MbUnit v3.2 viene con convenientes verificadores de contrato que pueden probar su implementación de GetHashCode()
e IEquatable<T>
. Más específicamente, puede estar interesado en EqualityContract
y HashCodeAcceptanceContract
. Vea here , here y there para más detalles.
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;
}
}
Luego declaras tu verificador de contrato así:
[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))
};
}