web-dev-qa-db-fra.com

Comment Assert.Areeequal détermine l'égalité entre deux ienumerables génériques?

J'ai un test d'unité pour vérifier si une méthode renvoie le correct IEnumerable. La méthode construit l'enumérable utilisation yield return. La classe qu'il est un énorme est ci-dessous:

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;
    }
}

C'est la partie pertinente de la méthode:

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

Si je stocke le résultat de cette méthode en actual, faites un autre énumérable expected et comparez-les comme ceci ...

  Assert.AreEqual(expected, actual);

..., l'assertion échoue.

J'ai écrit une méthode d'extension pour IEnumerable qui est similaire à Python's Zip fonction (il combine deux ienumerables dans un ensemble de paires) et essayé ceci:

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

Ça a marché! Alors quelle est la différence entre ces deux Assert.AreEquals?

35
Jason Baker

Assert.AreEqual va comparer les deux objets à portée de main. IEnumerables sont des types d'eux-mêmes et fournissent un mécanisme à itérer sur une collection ... mais ils ne sont pas en réalité cette collection. Votre comparaison initiale a comparé deux IEnumerables, qui est une comparaison valide ... mais pas ce dont vous aviez besoin. Vous avez besoin de comparer ce que les deux IEnumerables étaient destinés à énumérer.

Voici comment je compare deux énumérables:

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);
}

Je ne suis pas sûr que ce qui précède soit moins de code que votre .Zip méthode, mais c'est à peu près aussi simple que possible.

25
jrista

Trouvé:

Assert.IsTrue(expected.SequenceEqual(actual));
88
Jason Baker

Avez-vous envisagé d'utiliser la classe CollectionAssert de catégorie ... Considérant qu'il est destiné à effectuer des contrôles d'égalité sur les collections?

addendain :
[.____] si les "collections" sont comparées sont des énumérations, alors simplement les envelopper avec 'new List<T>(enumeration)' est le moyen le plus simple d'effectuer la comparaison. Construire une nouvelle liste provoque des frais généraux bien sûr, mais dans le contexte d'un test unitaire, cela ne devrait pas trop importer, j'espère?

48
jerryjvl

Je pense que le moyen le plus simple et le plus clair d'affirmer l'égalité que vous voulez est une combinaison de la réponse de Jerryjvl et de commenter sur son poste de Memark - Combinez CollectionAssert.AreEqual avec des méthodes d'extension:

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

Cela donne des informations d'erreur plus riches que la réponse séquence que suggérée par l'OP (elle vous indiquera quel élément a été trouvé inattendu). Par exemple:

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.   

Vous serez vraiment Heureux que vous l'avez fait de cette façon si/lorsque votre test échoue - parfois, vous pouvez même savoir ce qui est faux sans avoir à rompre le débogueur - et hey vous faites TDD à droite, alors Vous écrivez d'abord un test d'échec, non? ;-)

Les messages d'erreur sont encore plus utiles si vous utilisez AreEquivalent pour tester l'équivalence (commande n'a pas d'importance):

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).   
19
bacar