2009-06-01 9 views
35

Tengo una prueba de unidad para comprobar si un método devuelve el IEnumerable correcto. El método crea el enumerable usando yield return. La clase que se trata de un enumerable de es el siguiente:¿Cómo determina Assert.AreEqual la igualdad entre dos IEnumerables genéricos?

enum TokenType 
{ 
    NUMBER, 
    COMMAND, 
    ARITHMETIC, 
} 

internal class Token 
{ 
    public TokenType type { get; set; } 
    public string text { get; set; } 
    public static bool operator == (Token lh, Token rh) { return (lh.type == rh.type) && (lh.text == rh.text); } 
    public static bool operator != (Token lh, Token rh) { return !(lh == rh); } 
    public override int GetHashCode() 
    { 
     return text.GetHashCode() % type.GetHashCode(); 
    } 
    public override bool Equals(object obj) 
    { 
     return this == (Token)obj; 
    } 
} 

Esta es la parte relevante del método:

foreach (var lookup in REGEX_MAPPING) 
{ 
    if (lookup.re.IsMatch(s)) 
    { 
     yield return new Token { type = lookup.type, text = s }; 
     break; 
    } 
} 

Si almaceno el resultado de este método en actual, haga otra enumerables expected, y compárelos así ...

Assert.AreEqual(expected, actual); 

..., la aserción falla.

me escribió un método de extensión para IEnumerable que es similar a Python's zip function (que combina dos IEnumerables en un conjunto de pares) y probamos este:

foreach(Token[] t in expected.zip(actual)) 
{ 
    Assert.AreEqual(t[0], t[1]); 
} 

Funcionó! Entonces, ¿cuál es la diferencia entre estos dos Assert.AreEqual s?

+1

@ Jason panadero: por favor no tomes a mal De esta manera, ¿has considerado que el hecho de que tengas que hacer una pregunta como esta podría significar que estás complicando las cosas? –

+1

Erm ... no, en realidad no. :-) ¿Podrías dirigirme hacia donde estoy complicando las cosas? –

+0

Además, no estoy convencido de que usar "text.GetHashCode()% type.GetHashCode();" como un valor de retorno para GetHashCode() es una buena idea ... –

Respuesta

25

Assert.AreEqual va a comparar los dos objetos a mano. IEnumerable s son tipos en sí mismos y proporcionan un mecanismo para iterar sobre una colección ... pero en realidad no son esa colección. Su comparación original comparó dos IEnumerable s, que es una comparación válida ... pero no lo que necesita. Necesitaba comparar con lo que los dos IEnumerable estaban destinados a enumerar.

Aquí es cómo comparar dos enumerables:

Assert.AreEqual(t1.Count(), t2.Count()); 

IEnumerator<Token> e1 = t1.GetEnumerator(); 
IEnumerator<Token> e2 = t2.GetEnumerator(); 

while (e1.MoveNext() && e2.MoveNext()) 
{ 
    Assert.AreEqual(e1.Current, e2.Current); 
} 

no estoy seguro de si lo anterior es menos código que su método de .Zip, pero es tan simple como parece.

+0

Esto tiene sentido. ¿No hay forma de comparar los dos objetos de IEnumerables por objeto sin tener que escribir tanto código como tomó? –

+0

Solo usaría un ciclo while. He actualizado mi respuesta con un ejemplo. – jrista

+0

Encontré otra manera que funciona y es más simple. Voy a aceptar esto ya que demostraste por qué mi código no estaba funcionando. :-) –

86

lo encontró:

Assert.IsTrue(expected.SequenceEqual(actual)); 
+0

¡Buen hallazgo! Ni siquiera sabía que había una extensión SequenceEqual(). Ty! – jrista

+0

Muchas gracias. Guardó mis bucles ;-) –

+3

Esto no le informará sobre qué elemento fue desigual cuando falla la prueba; solo te dirá que falló. El mecanismo CollectionAssert sugerido por jerryjvl brinda información de fallas mucho más rica. – bacar

47

¿Ha considerado el uso de la clase CollectionAssert lugar ... teniendo en cuenta que se pretende realizar comprobaciones de igualdad en las colecciones?

Adición:
Si las 'colecciones' que se comparan son enumeraciones, a continuación, sólo tiene que envolver con 'new List<T>(enumeration)' es la forma más fácil para realizar la comparación. La construcción de una nueva lista ocasiona cierta sobrecarga, por supuesto, pero en el contexto de una prueba unitaria esto no debería importar demasiado, ¿no?

+0

Todos los métodos en CollectionAssert están configurados para comparar contra ICollections. Tengo IEnumerables. ¿Hay alguna manera fácil de cambiarlos a ICollections? –

+0

En casos como este, normalmente hago un resumen rápido en una 'nueva lista (enumeración)' para poder realizar la comparación. No es probable que la sobrecarga sea un problema en el contexto de una prueba unitaria. – jerryjvl

+6

Simplemente haga .ToArray() en ambos argumentos cuando use CollectionAssert. – MEMark

18

Creo que la forma más sencilla y clara para afirmar la igualdad quieres es una combinación de la respuesta por jerryjvl y comentario de su publicación por MEMark - combinar CollectionAssert.AreEqual con los métodos de extensión:

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray()); 

Esto da error más rico información que la respuesta de SequenceEqual sugerida por el OP (le dirá qué elemento se encontró que fue inesperado).Por ejemplo:

IEnumerable<string> expected = new List<string> { "a", "b" }; 
IEnumerable<string> actual = new List<string> { "a", "c" }; // mismatching second element 

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray()); 
// Helpful failure message! 
// CollectionAssert.AreEqual failed. (Element at index 1 do not match.)  

Assert.IsTrue(expected.SequenceEqual(actual)); 
// Mediocre failure message: 
// Assert.IsTrue failed. 

Usted va a ser realmente agradó que lo hizo de esta manera si/cuando la prueba falla - a veces incluso se puede saber lo que está mal sin tener que romper el depurador - y oye, estás haciendo TDD correctamente, entonces usted escribe una prueba de falla primero, ¿verdad? ;-)

Los mensajes de error se ponen aún más útil si usted está utilizando AreEquivalent para la prueba de equivalencia (el orden no importa):

CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList()); 
// really helpful error message! 
// CollectionAssert.AreEquivalent failed. The expected collection contains 1 
// occurrence(s) of <b>. The actual collection contains 0 occurrence(s). 
Cuestiones relacionadas