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

21

Ich teste, dass eine Funktion das tut, was von einer Liste erwartet wird. Also will 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

Um dies zu tun, was ist der beste Ansatz?

  • Testen aller Fälle im selben (Methoden-) Test unter dem Namen "WorksAsExpected"
  • Einen Test für jeden Fall platzieren, also haben
    • "WorksAsExpectedWhenNull"
    • "WorksAsExpectedWhenEmpty"
    • "WorksAsExpectedWhenSingleElement"
    • "WorksAsExpectedWhenMoreElements"
  • Eine andere Wahl, an die ich nicht gedacht habe :-)
malarres
quelle
2
Ich würde diese als separate Testfälle schreiben. Sie können parametrisierte Tests verwenden, wenn Ihr Testsystem dies unterstützt.
Jonrsharpe
5
Wenn Sie Ihre Tests in einem vorgegebenen Format schreiben ... Wann ... Dann wird es offensichtlich, dass sie wirklich separat getestet werden sollten ...
Robbie Dee
1
Ich möchte nur hinzufügen: IMO, es ist gut, Randfälle (wie null und leer) in separate Tests zu unterteilen, da diese häufig eine Sonderfalllogik für verschiedene mögliche Implementierungen beinhalten. Wenn diese Tests fehlschlagen, wird dies deutlich angezeigt Auf welche Weise schlägt der zu testende Code fehl (Sie müssen nicht tiefer graben oder den Testfall debuggen, um herauszufinden, was los ist).
Filip Milovanović
1
Liste mit doppelten Elementen?
Atayenel,

Antworten:

30

Die einfache Faustregel, die ich verwende, um eine Reihe von Tests in einem oder mehreren Testfällen durchzuführen, lautet: Handelt es sich nur um eine Konfiguration?

Wenn ich also teste, dass für mehrere Elemente beide Elemente 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. Daher würde ich in diesem Fall definitiv mehrere Tests erstellen.

Wie andere bereits erwähnt haben, können diese "Mehrfachtests" als ein einziger parametrisierter Testfall existieren. dh der gleiche Testfall wird mit einer 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 Zusicherungen ausführen können, verwenden Sie diesen Ansatz. Wenn Sie feststellen, dass Sie ifbeispielsweise 's hinzufügen , um unterschiedlichen Code für unterschiedliche Teile dieser Daten auszuführen, ist dies nicht die Lösung. Verwenden Sie im letzteren Fall einzelne Testfälle.

David Arno
quelle
14

Es gibt einen Kompromiss. Je mehr Sie in einem Test einpacken, desto wahrscheinlicher ist es, dass Sie einen Zwiebeleffekt haben, der versucht, ihn zum Bestehen zu bringen. Mit anderen Worten, der erste Fehler beendet 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 sich bis auf den Setup-Code größtenteils ähneln, ist eine Menge an 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 über ein Feld oder über eine statische Methode, mit der die Daten generiert werden. Wenn einige Ihrer Fakten unter bestimmten Voraussetzungen zutreffen und andere nicht das Konzept einer Annahme einführen . Sie Assume.that()überspringen einfach den Test für die Daten, wenn die Vorbedingung nicht erfüllt ist. Auf diese Weise können Sie "Funktioniert wie erwartet" definieren und dann einfach eine Menge Daten einspeisen. 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 für Theorien haben können. Das Endergebnis ist das gleiche. Wenn Sie die Ergebnisse anzeigen, verfügen Sie über einen übergeordneten Eintrag für den Test selbst und anschließend über einen bestimmten Eintrag für jeden Datenpunkt.
  • Ein Test mit mehreren Aussagen . Die Einrichtung nimmt weniger Zeit in Anspruch, aber es kommt immer wieder vor, dass Probleme entdeckt werden. Wenn der Test zu lang ist und zu viele verschiedene Szenarien getestet werden, gibt es zwei große Risiken: Es wird zu lange dauern, bis der Test ausgeführt wird, und Ihr Team wird die Nase voll davon haben und den Test ausschalten.
  • Mehrere Tests mit ähnlicher Implementierung . Es ist wichtig zu beachten, dass sich die Tests nicht überlappen, wenn die Aussagen unterschiedlich sind. Dies wäre jedoch die übliche Weisheit eines TDD-Teams.

Ich bin nicht der festen Überzeugung, dass assertIhr Test nur eine Aussage enthalten kann, aber ich setze die Einschränkungen dahingehend, dass alle Behauptungen die Nachbedingungen einer einzelnen Aktion testen sollten. Wenn der einzige Unterschied zwischen den Tests Daten sind, bin ich der Meinung, dass ich die fortschrittlicheren datengesteuerten Testfunktionen wie parametrisierte Tests oder Theorien verwenden sollte.

Wägen Sie Ihre Optionen ab, um das beste Ergebnis zu erzielen. Ich werde sagen, dass "WorksAsExpectedWhenNull" sich grundlegend von allen Fällen unterscheidet, in denen Sie mit einer Sammlung zu tun haben, die eine unterschiedliche Anzahl von Elementen aufweist.

Berin Loritsch
quelle
5

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 Test-Framework die Parametrisierung nicht unterstützt, extrahieren Sie den gemeinsam genutzten Code in eine Hilfsfunktion und rufen Sie ihn aus einzelnen Testfällen auf.

Vermeiden Sie die Parametrisierung über eine Schleife innerhalb eines Testfalls, da dadurch nur schwer festgestellt werden kann, 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 das Testen von Eigenschaften . Sie würden verschiedene (parametrisierte) Tests erstellen, die verschiedene Eigenschaften Ihrer Funktion bestätigen, ohne konkrete Eingabedaten anzugeben. Eine Eigenschaft könnte zB sein: Für alle Listen xshat die Liste ys = f(xs)die gleiche Länge wie xs. Das Testframework generiert dann interessante Listen und Zufallslisten und bestätigt, dass Ihre Eigenschaften für alle gelten. Dies geht weg von der manuellen Angabe von Beispielen, da durch die manuelle Auswahl von Beispielen interessante Randfälle übersehen werden können.

amon
quelle
Sollte das "Miss" im letzten Satz nicht "find" sein?
Robbie Dee
@ RobbieDee Englisch ist mehrdeutig, behoben.
Amon
3

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 Zusicherungen für dasselbe Objekt haben. In der Regel handelt es sich dabei um dasselbe Konzept, 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, sich an das Muster "Anordnen, Handeln, Bestätigen" zu halten.

Der Schlüssel ist, dass Sie nur eine Aktion haben und dann die Ergebnisse dieser Aktion mit Asserts überprüfen. Aber es ist "Anordnen, Handeln, Bestätigen, Testende". Wenn Sie versucht sind, mit dem Testen fortzufahren, indem Sie eine weitere Aktion ausführen, und anschließend weitere Asserts ausführen, führen Sie stattdessen einen separaten Test durch. Quelle

Sonny
quelle
0

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

  • Wenn Ihr Test nur 1 Bedingung hat, um den Test einzurichten, aber viele Nebenwirkungen. Multi-Assert ist akzeptabel.
  • Wenn Sie jedoch mehrere Bedingungen haben, bedeutet dies, dass Sie mehrere Testfälle haben, von denen jeder nur durch einen Einheitentest abgedeckt werden sollte.
HungDL
quelle
Dies liest sich eher wie ein Kommentar, siehe Wie man antwortet
Mücke