it-swarm.com.de

Eine Liste testen ... Alle im selben Test oder ein Test für jede Bedingung?

Ich teste, dass eine Funktion das tut, was auf einer Liste erwartet wird. Also möchte ich testen

f(null) -> null
f(empty) -> empty
f(list with one element) -> list with one element
f(list with 2+ elements) -> list with the same number of elements, doing what expected

Was ist dazu der beste Ansatz?

  • Testen aller Fälle im selben (Methoden-) Test unter dem Namen "WorksAsExpected"
  • Platzieren eines Tests für jeden Fall mit
    • "WorksAsExpectedWhenNull"
    • "WorksAsExpectedWhenEmpty"
    • "WorksAsExpectedWhenSingleElement"
    • "WorksAsExpectedWhenMoreElements"
  • Eine andere Wahl, an die ich nicht gedacht habe :-)
21
malarres

Die einfache Faustregel, nach der ich eine Reihe von Tests in einem oder mehreren Testfällen durchführen möchte, lautet: Handelt es sich nur um eine Einrichtung?

Wenn ich also getestet habe, dass für mehrere Elemente beide verarbeitet und das richtige Ergebnis abgeleitet wurden, habe ich möglicherweise zwei oder mehr Asserts, aber ich muss die Liste nur einmal einrichten. Ein Testfall ist also in Ordnung.

In Ihrem Fall müsste ich jedoch eine Nullliste, eine leere Liste usw. einrichten. Das sind mehrere Setups. In diesem Fall würde ich also definitiv mehrere Tests erstellen.

Wie andere bereits erwähnt haben, können diese "Mehrfachtests" möglicherweise als ein einziger parametrisierter Testfall existieren. Das heißt, der gleiche Testfall wird für eine Vielzahl von Setup-Daten ausgeführt. Der Schlüssel zum Wissen, ob dies eine praktikable Lösung ist, liegt in den anderen Teilen des Tests: "Aktion" und "Behauptung". Wenn Sie für jeden Datensatz dieselben Aktionen und Asserts ausführen können, verwenden Sie diesen Ansatz. Wenn Sie beispielsweise if hinzufügen, um unterschiedlichen Code für verschiedene Teile dieser Daten auszuführen, ist dies nicht die Lösung. Verwenden Sie in letzterem Fall einzelne Testfälle.

30
David Arno

Es gibt einen Kompromiss. Je mehr Sie in einem Test verpacken, desto wahrscheinlicher ist es, dass Sie einen Zwiebeleffekt haben, der versucht, ihn zu bestehen. Mit anderen Worten, der allererste Fehler stoppt diesen Test. Sie werden nichts über die anderen Behauptungen wissen, bis Sie den ersten Fehler behoben haben. Das heißt, eine Reihe von Unit-Tests zu haben, die bis auf den Setup-Code größtenteils ähnlich sind, ist eine Menge Arbeit, nur um herauszufinden, dass einige wie geschrieben funktionieren und andere nicht.

Mögliche Tools, basierend auf Ihrem Framework:

  • Theorien. Mit einer Theorie können Sie eine Reihe von Fakten zu einem Datensatz testen. Das Framework füttert Ihre Tests dann mit mehreren Testdatenszenarien - entweder durch ein Feld oder durch eine statische Methode, die die Daten generiert. Wenn einige Ihrer Fakten aufgrund einiger Voraussetzungen zutreffen und andere nicht, führen diese Frameworks das Konzept einer Annahme ein. Ihre Assume.that() überspringt einfach den Test für die Daten, wenn die Voraussetzung nicht erfüllt ist. Auf diese Weise können Sie "Funktioniert wie erwartet" definieren und dann einfach viele Daten eingeben. Wenn Sie die Ergebnisse anzeigen, haben Sie einen Eintrag für die übergeordneten Tests und dann einen Untereintrag für jedes Datenelement.
  • Parametrisierte Tests. Parametrisierte Tests waren ein Vorläufer von Theorien, daher gibt es möglicherweise nicht die Voraussetzungsprüfung, die Sie mit Theorien durchführen können. Das Endergebnis ist das gleiche. Wenn Sie die Ergebnisse anzeigen, haben Sie einen übergeordneten Eintrag für den Test selbst und dann einen bestimmten Eintrag für jeden Datenpunkt.
  • Ein Test mit mehreren Asserts. Das Einrichten dauert weniger lange, aber Sie werden am Ende immer wieder Probleme aufdecken. Wenn der Test zu lang ist und zu viele verschiedene Szenarien getestet wurden, gibt es zwei große Risiken: Die Ausführung dauert zu lange, und Ihr Team hat es satt und schaltet den Test aus.
  • Mehrere Tests mit ähnlicher Implementierung. Es ist wichtig zu beachten, dass sich die Tests bei unterschiedlichen Behauptungen nicht überschneiden. Dies wäre jedoch die übliche Weisheit eines TDD-fokussierten Teams.

Ich bin nicht der strengen Meinung, dass es in Ihrem Test nur eine assert -Anweisung geben kann, aber ich lege die Einschränkungen fest, dass alle Behauptungen die Nachbedingungen einer einzelnen Aktion testen sollten. Wenn der einzige Unterschied zwischen den Tests Daten sind, bin ich der Meinung, die erweiterten datengesteuerten Testfunktionen wie parametrisierte Tests oder Theorien zu verwenden.

Wägen Sie Ihre Optionen ab, um zu entscheiden, was das beste Ergebnis ist. Ich werde sagen, dass "WorksAsExpectedWhenNull" sich grundlegend von allen Fällen unterscheidet, in denen Sie mit einer Sammlung arbeiten, die eine unterschiedliche Anzahl von Elementen enthält.

14
Berin Loritsch

Das sind verschiedene Testfälle, aber der Code für den Test ist der gleiche. Die Verwendung parametrisierter Tests ist daher die beste Lösung. Wenn Ihr Testframework die Parametrisierung nicht unterstützt, extrahieren Sie den gemeinsam genutzten Code in eine Hilfsfunktion und rufen Sie ihn aus einzelnen Testfällen auf.

Versuchen Sie, die Parametrisierung über eine Schleife innerhalb eines Testfalls zu vermeiden, da es schwierig ist, festzustellen, welcher Datensatz den Fehler verursacht hat.

In Ihrem TDD-Rot-Grün-Refaktor-Zyklus sollten Sie jeweils einen Beispieldatensatz hinzufügen. Das Kombinieren mehrerer Testfälle zu einem parametrisierten Test wäre Teil des Refactoring-Schritts.

Ein etwas anderer Ansatz ist Eigenschaftstest . Sie würden verschiedene (parametrisierte) Tests erstellen, die verschiedene Eigenschaften Ihrer Funktion bestätigen, ohne konkrete Eingabedaten anzugeben. Z.B. Eine Eigenschaft könnte sein: Für alle Listen xs hat die Liste ys = f(xs) die gleiche Länge wie xs. Das Testframework würde dann interessante Listen und Zufallslisten generieren und behaupten, dass Ihre Eigenschaften für alle gelten. Dies entfernt sich von der manuellen Angabe von Beispielen, da bei der manuellen Auswahl von Beispielen interessante Edge-Fälle übersehen werden könnten.

5
amon

Ein Test für jeden Fall ist angemessen, da das Testen eines einzelnen Konzepts in jedem Test eine gute Richtlinie ist, die häufig empfohlen wird.

Siehe diesen Beitrag: Ist es in Ordnung, mehrere Asserts in einem einzigen Unit-Test zu haben? . Dort gibt es auch eine relevante und detaillierte Diskussion:

Meine Richtlinie ist normalerweise, dass Sie ein logisches KONZEPT pro Test testen. Sie können mehrere Asserts für dasselbe Objekt haben. Sie werden normalerweise das gleiche Konzept sein, das getestet wird. Quelle - Roy Osherove

[...]

Tests sollten nur aus einem Grund fehlschlagen, aber das bedeutet nicht immer, dass es nur eine Assert-Anweisung geben sollte. IMHO ist es wichtiger, an dem Muster "Anordnen, Handeln, Bestätigen" festzuhalten.

Der Schlüssel ist, dass Sie nur eine Aktion haben und dann die Ergebnisse dieser Aktion mithilfe von Asserts überprüfen. Aber es ist "Anordnen, handeln, behaupten, Testende". Wenn Sie versucht sind, den Test fortzusetzen, indem Sie eine andere Aktion ausführen und anschließend weitere Asserts ausführen, machen Sie diesen Test stattdessen separat. Quelle

3
sonny

Meiner Meinung nach hängt es von den Testbedingungen ab.

  • Wenn Ihr Test nur eine Bedingung zum Einrichten des Tests hat, aber viele Nebenwirkungen. Multi-Assert ist akzeptabel.
  • Wenn Sie jedoch mehrere Bedingungen haben, bedeutet dies, dass Sie mehrere Testfälle haben, die jeweils nur von einem Einheitentest abgedeckt werden sollten.
0
HungDL