Prüfung der Aufteilungseinheiten nach Anforderung oder Methode

16

Erstens, Entschuldigung für den Titel, ich konnte mir nicht vorstellen, wie ich es am einfachsten erklären könnte!

Ich habe eine Methode, für die ich Komponententests schreiben möchte. Ich werde es ziemlich allgemein halten, da ich nicht auf die Implementierung der Methode, sondern nur auf das Testen derselben eingehen möchte. Die Methode ist:

public void HandleItem(item a)
{         
     CreateNewItem();
     UpdateStatusOnPreviousItem();
     SetNextRunDate();
}

Diese Klasse hat also eine öffentliche Methode, die dann einige private Methoden aufruft, um die Logik auszuführen.

Wenn ich den Unit-Test schreibe, möchte ich überprüfen, ob alle drei Dinge erledigt wurden. Da sie alle im selben Lauf aufgerufen werden, dachte ich, dass ich es als einen Test machen könnte:

public void GivenItem_WhenRun_Thenxxxxx
{
     HandleItem(item);
     // Assert item has been created
     // Assert status has been set on the previous item
     // Assert run date has been set
}

Aber ich dachte, ich könnte es auch als drei separate Tests schreiben:

public void GivenItem_WhenRun_ThenItemIsCreated()
{
    HandleItem(item);
}

public void GivenItem_WhenRun_ThenStatusIsUpdatedOnPreviousItem()
{
   HandleItem(item);
}

public void GivenItem_WhenRun_ThenRunDateIsSet()
{
     HandleItem(item);
}

Das scheint mir besser zu sein, da es sich im Wesentlichen um eine Auflistung der Anforderungen handelt, aber dann hängen alle drei zusammen und erfordern genau die gleiche Arbeit, die mit der getesteten Methode ausgeführt wurde. Daher muss derselbe Code dreimal ausgeführt werden.

Gibt es einen empfohlenen Ansatz dafür?

Vielen Dank

ADringer
quelle

Antworten:

29

Es gibt einen subtilen Unterschied zwischen beiden Ansätzen. Im ersten Fall, wenn der erste Assertfehlschlägt, werden die beiden anderen nicht mehr ausgeführt. Im zweiten Fall werden immer alle drei Tests ausgeführt, auch wenn einer fehlschlägt. Abhängig von der Art der getesteten Funktionalität passt dies möglicherweise nicht zu Ihrem Fall:

  • Wenn es sinnvoll ist, die drei Asserts unabhängig voneinander auszuführen, da bei einem Ausfall die anderen beiden möglicherweise immer noch nicht fehlschlagen, hat der zweite Ansatz den Vorteil, dass Sie die vollständigen Testergebnisse für alle drei Tests in einem Durchgang erhalten. Dies kann hilfreich sein, wenn Sie erhebliche Build-Zeiten haben, da Sie so die Möglichkeit haben, bis zu 3 Fehler gleichzeitig zu beheben, bevor Sie den nächsten Build ausführen.

  • Wenn jedoch ein Fehlschlagen des ersten Tests immer bedeutet, dass die beiden anderen Tests ebenfalls fehlschlagen, ist es wahrscheinlich besser, den ersten Ansatz zu verwenden (da es wenig sinnvoll ist, einen Test durchzuführen, wenn Sie dies bereits im Voraus wissen Scheitern).

Doc Brown
quelle
2
+1, guter Punkt. Mir ist nicht in den Sinn gekommen, dass Bauzeiten auch ein Engpass sein können.
Kilian Foth
1
@KilianFoth: Sie arbeiten nicht oft genug in C ++ :(
Matthieu M.
1
@ MatthieuM .: um fair zu sein, ist die Frage mit "C #" markiert
Doc Brown
10

Kurze Antwort: Es ist viel wichtiger, dass Ihre Tests alle Funktionen abdecken, als wie sie es tun.

Längere Antwort: Wenn Sie immer noch zwischen diesen weitgehend gleichwertigen Lösungen wählen möchten, können Sie zusätzliche Kriterien für das Beste verwenden. Zum Beispiel,

  • Lesbarkeit: Wenn die Methode viele Dinge ausführt, die nicht eng miteinander zusammenhängen, ist ein kombinierter Test möglicherweise schwer zu verstehen. Die Methode selbst ist jedoch möglicherweise schwer zu verstehen. Sie sollten die Methode also überarbeiten und nicht den Test!)
  • Effizienz: Wenn die Ausführung der Methode lange dauert, ist dies möglicherweise ein schwacher Grund, alle drei Prüfungen zu kombinieren, um Zeit zu sparen
  • Effizienz2: Wenn das Ausführen des Setup-Codes Ihres Frameworks lange dauert, kann dies auch ein schwacher Grund sein, um mehrere Testmethoden zu vermeiden. (Wenn dies jedoch wirklich ein Problem ist, sollten Sie wahrscheinlich Ihre Testkonfiguration korrigieren oder ändern - Regressionstests verlieren viel an Wert, wenn Sie sie nicht blitzschnell ausführen können.)
Kilian Foth
quelle
2

Verwenden Sie einen Methodenaufruf mit mehreren Zusicherungen. Hier ist der Grund:

Wenn Sie HandleItem (a) testen, testen Sie, dass die Methode das Element in den richtigen Zustand versetzt hat. Stellen Sie sich statt "eine Aussage pro Test" "ein logisches Konzept pro Test" vor.

Frage: Wenn ein CreateNewItem fehlschlägt, die beiden anderen Methoden jedoch erfolgreich sind, bedeutet dies, dass HandleItem erfolgreich abgeschlossen wurde? Ich vermute nicht.

Mit mehreren Zusicherungen (mit entsprechenden Meldungen) wissen Sie genau, was fehlgeschlagen ist. In der Regel testen Sie eine Methode mehrmals auf mehrere Eingaben oder Eingabezustände, um mehrere Zusicherungen nicht zu vermeiden.

IMO, diese Fragen sind in der Regel ein Zeichen für etwas anderes. Es ist ein Zeichen dafür, dass HandleItem nicht wirklich ein "Unit-Test" ist, da es nur an andere Methoden zu delegieren scheint. Wenn Sie einfach überprüfen, ob HandleItem andere Methoden korrekt aufruft, wird es eher zu einem Integrationstestkandidaten (in diesem Fall hätten Sie noch 3 Asserts).

Möglicherweise möchten Sie die anderen 3 Methoden veröffentlichen und unabhängig testen. Oder sie sogar in eine andere Klasse extrahieren.

Kevin
quelle
0

Verwenden Sie den 2. Ansatz. Wenn der Test bei Ihrem ersten Ansatz fehlschlägt, wissen Sie nicht sofort, warum. Dies könnte eine der drei Funktionen sein, die fehlgeschlagen sind. Mit dem zweiten Ansatz wissen Sie sofort, wo das Problem aufgetreten ist. Sie können doppelten Code in Ihre Test-Setup-Funktion einfügen.

Eternal21
quelle
-1

IMHO sollten Sie die drei Teile dieser Methode separat testen, damit Sie genauer wissen, wo Probleme auftreten, wenn sie auftreten, und dabei vermeiden, denselben Teil Ihres Codes zweimal zu überprüfen.

SuperUser
quelle
-2

Ich glaube nicht, dass es ein gutes Argument dafür gibt, separate Testmethoden für Ihren Anwendungsfall zu schreiben. Wenn Sie die Ergebnisse aller drei variablen Bedingungen erhalten möchten, können Sie alle drei Bedingungen testen und ihre Fehler ausdrucken, indem Sie sie in a verketten stringund feststellen, ob die Zeichenfolge nach Abschluss des Tests noch leer ist. Indem Sie alle in derselben Methode belassen, dokumentieren Ihre Bedingungen und Fehlermeldungen die erwartete Nachbedingung der Methode an einem Ort, anstatt sie in drei Methoden aufzuteilen, die später möglicherweise getrennt werden.

Dies bedeutet, dass Ihr einzelner Test mehr Code enthält. Wenn Sie jedoch so viele Nachbedingungen haben, dass dies ein Problem darstellt, möchten Sie wahrscheinlich Ihre Testmethoden überarbeiten, um einzelne darin enthaltene Methoden zu testen HandleItem.

IllusiveBrian
quelle