Tests unitaires: assertions différées avec Linq

18

Est-il correct d'ajouter des assertions différées comme celle-ci

var actualKittens = actualKittens.Select(kitten => {
    Assert.IsСute(kitten);
    return kitten
});

Pourquoi? Je peux donc répéter une seule fois, même avec des déclarations qui attendent une collecte matérialisée, par exemple:

CollectionAssert.AreEquivalent(expectedKittens, actualKittens.ToList());

Et cela pourrait aussi être non seulement Select mais une méthode avec un itérateur défini et ayant beaucoup de contrôles et de logique (par exemple un certain comptage et filtrage).

Le germe du doute est la complexité de la lecture et du débogage d'un tel code en cas d'échec du test.

SerG
la source
1
Je ne me fierais pas à cela pour les tests, mais il est parfois OK de le faire dans le code de production s'il n'y a pas de meilleure solution. Vous pouvez vous faire une fonction d'aide sequence.WithSideEffect(item => Assert.IsCute(item))pour le rendre plus propre.
usr
@usr On dirait que vous avez attrapé la faille clé de cette façon - Effet secondaire d'un itérateur.
SerG
N'avez-vous pas encore à répéter deux fois, une fois pour générer la liste et à nouveau pour la comparer expectedKittens? Vous venez de masquer les itérations derrière les appels de méthode.
IllusiveBrian
@IllusiveBrian Dans ce sens, dans l'exemple, oui. C'est encore moins qu'avec des supplémentaires .All().
SerG

Réponses:

37

Est-il correct d'ajouter des assertions différées comme celle-ci [..]

Non , ça ne l'est pas. Pourquoi? Parce que si, pour une raison quelconque, vous supprimez la deuxième affirmation, le test deviendrait toujours vert et vous penseriez qu'il fonctionne toujours, mais ce n'est pas le cas car la collection ne sera pas énumérée. Si vous avez deux ou plusieurs assertions indépendantes, elles continueront de faire leur travail même si vous en désactivez une.

Considérez cette combinaison:

Assert.IsTrue(actualKittens.All(x => x.IsCute());
CollectionAssert.AreEquivalent(expectedKittens, actualKittens.ToList());

Maintenant, même si vous désactivez ou supprimez l'une des assertions, l'autre ferait toujours son travail. De plus, si vous oubliez de matérialiser la collection, son exécution peut prendre plus de temps, mais elle fonctionnera toujours. Les tests indépendants sont plus robustes et fiables.

Il y a aussi un deuxième non . Je ne sais pas comment les autres frameworks le gèrent, mais si vous utilisez la plate-forme MS Test, vous ne saurez pas quel test a échoué. Si vous double-cliquez sur le test qui a échoué, cela vous montrera que le test a CollectionAssertéchoué, mais en réalité, c'est le nid Assertqui s'est mal passé et il sera extrêmement difficile à déboguer. Voici un exemple:

    [TestMethod]
    public void TestMethod()
    {
        var numbers = new[] { 1, 2, 3 }.Select(x =>
        {
            Assert.Fail("Wrong number.");
            return x;
        });

        // This will fail and you won't be sure why.
        CollectionAssert.AreEqual(new[] { 1, 2, 3 }, numbers.ToList()); 

    }

Cela signifie que le premier test est en fait inutile car il n'aide pas à trouver un bug. Vous ne savez pas s'il a échoué parce qu'un numéro n'était pas valide ou parce que les deux collections étaient différentes.


Pourquoi? Je peux donc répéter une seule fois, même avec des déclarations qui attendent une collecte matérialisée

Je me demande pourquoi vous vous en souciez? Ce sont des tests unitaires. Vous n'avez pas besoin d'optimiser chaque élément d'entre eux et généralement les tests ne nécessitent pas des millions d'éléments, donc les performances ne devraient pas être un problème.

Vous devrez maintenir de tels tests, alors pourquoi devriez-vous les rendre plus complexes que nécessaires? Écrivez des assertions simples qui fonctionnent.

t3chb0t
la source
Si, pour une raison quelconque, une assertion doit être enfouie dans le flux de contrôle, une façon de s'assurer qu'elle a été exécutée consiste à conserver un compteur / indicateur incrémenté / défini sur true avant l'assertion imbriquée. Plus tard, nous pouvons affirmer que le flux de contrôle attendu a été pris en vérifiant ce compteur. Pas parfait, mais répond largement à votre première critique.
amon
1
De plus, vous ou quelqu'un d'autre reviendriez à l'assertion différée dans 6 mois et devrez perdre du temps à le comprendre.
DavidTheWin
Il y a un problème avec votre exemple. L'appel ToListitérera l'énumérable, n'est-ce pas?
RubberDuck
1
@RubberDuck oui, cela échouera et échouera également non pas au Assert.Failmais au CollectionAssertet vous ne pourrez pas dire quelle affirmation a vraiment mal tourné. Je veux dire que VS ne se concentrera pas Assert.Failsur l'autre mais sur l'autre ... maintenant vous pouvez déboguer.
t3chb0t