Unit-Tests: Zurückgestellte Behauptungen mit Linq

18

Ist es in Ordnung, verzögerte Aussagen wie diese hinzuzufügen?

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

Warum? So kann ich auch mit Aussagen, die eine materialisierte Sammlung erwarten, nur einmal iterieren, zum Beispiel:

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

Es könnte sich auch nicht nur um Select handeln, sondern auch um eine Methode mit einem definierten Iterator und vielen Überprüfungen und Logik (z. B. Zählen und Filtern).

Der Keim des Zweifels ist die Komplexität des Lesens und Debuggens eines solchen Codes im Falle eines Testfehlers.

SerG
quelle
1
Ich würde mich beim Testen nicht darauf verlassen , aber es ist manchmal in Ordnung, dies im Produktionscode zu tun, wenn es keine bessere Lösung gibt. Sie können sich eine Hilfsfunktion erstellen sequence.WithSideEffect(item => Assert.IsCute(item)), um es sauberer zu machen.
USR
@usr Sieht so aus, als hätten Sie den entscheidenden Fehler auf diese Weise entdeckt - Nebeneffekt durch einen Iterator.
SerG
Müssen Sie nicht noch zweimal iterieren, um die Liste einmal zu generieren und erneut zu vergleichen expectedKittens? Sie haben gerade die Iterationen hinter Methodenaufrufen ausgeblendet.
IllusiveBrian
@IllusiveBrian In diesem Sinne im Beispiel ja. Es ist immer noch weniger als mit zusätzlichen .All().
SerG

Antworten:

37

Ist es in Ordnung, zurückgestellte Behauptungen wie diese hinzuzufügen? [..]

Nein , das ist es nicht. Warum? Denn wenn Sie aus irgendeinem Grund die zweite Zusicherung entfernen, wird der Test immer noch grün und Sie würden denken, dass er immer noch funktioniert, aber nicht, da die Auflistung nicht aufgezählt wird. Wenn Sie zwei oder mehr unabhängige Behauptungen haben, werden diese ihre Arbeit auch dann fortsetzen, wenn Sie eine davon deaktivieren.

Betrachten Sie diese Kombination:

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

Selbst wenn Sie jetzt eine der Zusicherungen deaktivieren oder entfernen, würde die andere weiterhin ihre Aufgabe erfüllen. Auch wenn du vergessen , die Sammlung zu materialisieren, dauert die Ausführung möglicherweise länger, funktioniert aber weiterhin. Unabhängige Tests sind robuster und zuverlässiger.

Es gibt auch eine zweite Nr . Ich bin nicht sicher, wie andere Frameworks damit umgehen, aber wenn Sie die MS Test-Plattform verwenden, wissen Sie nicht, welcher Test fehlgeschlagen ist. Wenn Sie auf den fehlgeschlagenen Test doppelklicken, wird angezeigt, dass der Test CollectionAssertfehlgeschlagen ist. In Wirklichkeit war es jedoch der verschachtelte Test , bei dem ein Fehler aufgetreten ist Assert, und das Debuggen ist äußerst schwierig. Hier ist ein Beispiel:

    [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()); 

    }

Dies bedeutet, dass der erste Test eigentlich nutzlos ist, da es nicht hilft, einen Fehler zu finden. Sie wissen nicht, ob es fehlgeschlagen ist, weil eine Zahl ungültig war oder weil beide Sammlungen unterschiedlich waren.


Warum? So kann ich auch mit Aussagen, die eine materialisierte Sammlung erwarten, nur einmal iterieren

Ich frage mich, warum es dir wichtig ist? Dies sind Unit-Tests. Sie müssen nicht jedes Bit optimieren, und für Tests sind in der Regel nicht Millionen von Elementen erforderlich, sodass die Leistung kein Problem darstellen sollte.

Sie müssen solche Tests beibehalten. Warum sollten Sie sie dann komplexer gestalten, als es erforderlich ist? Schreiben Sie einfache Aussagen, die funktionieren.

t3chb0t
quelle
Wenn aus irgendeinem Grund eine Zusicherung in den Kontrollfluss eingegraben werden muss, besteht eine Möglichkeit, um sicherzustellen, dass sie ausgeführt wurde, darin, einen Zähler / ein Flag vor der verschachtelten Zusicherung auf wahr zu setzen. Später können wir behaupten, dass der erwartete Kontrollfluss genommen wurde, indem wir diesen Zähler überprüfen. Nicht perfekt, geht aber weitgehend auf Ihre erste Kritik ein.
amon
1
Darüber hinaus würden Sie oder eine andere Person innerhalb von 6 Monaten auf die zurückgestellte Behauptung zurückgreifen und Zeit damit verschwenden, dies herauszufinden.
DavidTheWin
Mit deinem Beispiel stimmt etwas nicht. Das Aufrufen ToListwird das Aufzählbare durchlaufen, nicht wahr?
RubberDuck
1
@RubberDuck ja, es wird und wird auch scheitern aber nicht am Assert.Failsondern am CollectionAssertund du wirst nicht sagen können welche Aussage tatsächlich schief gelaufen ist. Ich meine, VS wird sich nicht Assert.Failauf das andere konzentrieren, sondern auf das andere. Jetzt können Sie debuggen.
t3chb0t