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 :-)
unit-testing
tdd
malarres
quelle
quelle
Antworten:
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
if
beispielsweise '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.quelle
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:
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.Ich bin nicht der festen Überzeugung, dass
assert
Ihr 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.
quelle
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
xs
hat die Listeys = f(xs)
die gleiche Länge wiexs
. 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.quelle
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:
[...]
quelle
Meiner Meinung nach hängt es von den Testbedingungen ab.
quelle