Ist es in diesem Fall töricht, sich an eine Behauptung pro Test zu halten?

10

Ich habe eine Klasse, die ich teste. Die Klasse hat eine Funktion:apply(List<IRule> rules, List<ITarget> targets);

In einem Test möchte ich sicherstellen, dass jedes Ziel an eine Regel übergeben wurde, a la:

rule1.AssertWasCalled(fnord => fnord.Test(target1));
rule1.AssertWasCalled(fnord => fnord.Test(target2));
rule1.AssertWasCalled(fnord => fnord.Test(target3));

Es scheint mir, dass es ein Hobgoblin wäre, mich auf eine einzige Aussage zu beschränken . Bin ich in dieser Annahme richtig oder gibt es eine andere Möglichkeit, um zu behaupten, dass jedes Ziel tatsächlich getestet wurde?

Wayne Werner
quelle
Ich kann die Fnords sehen!
Ross Patterson

Antworten:

15

Die drei Behauptungen sind im Wesentlichen ein Test. Sie testen das Verhalten einer Methode in einer Sammlung, um sicherzustellen, dass jedes Element ein Parameter für einen bestimmten Aufruf war (dh, dass jedes Element ordnungsgemäß verarbeitet wurde).

Das dreimalige Einrichten der Daten und drei verschiedene Methoden ist verschwenderisch und weniger lesbar als die Alternative, mehrere Asserts zu haben.

Bei der einzelnen Assert- "Regel" geht es mehr darum, Asserts unterschiedlichen Typs mit denselben Methoden zu erstellen (im Wesentlichen verschiedene Dinge zu testen). Dies gilt in diesem Fall nicht wirklich, wenn Sie auf ein einzelnes Verhalten testen.

Oded
quelle
3
In der Tat: Die Regel ist mehr eine logische Behauptung pro Unit Test. Sie können diese in einer höheren Ebene zusammenfassen und behaupten, dass Sie sie in verschiedenen Tests wiederverwenden können.
Laurent Bourgault-Roy
5

Ich bin davon überzeugt, dass diese eine Behauptung pro Testregel existiert, um Ihre Tests auf ein Problem zu konzentrieren. Wenn Sie 20 Dinge in einem Test testen, ist es wirklich schwer zu sagen, wie hoch Ihre Abdeckung ist. Sie wissen, dass es ein Problem verursacht, wenn Sie die Testmethode nicht ohne das Wort und darin benennen können. Wenn Ihre Testmethode beispielsweise genauer benannt wäre, testen testFooIsTrueAndDbExistsAndBarIsNullAndAnExceptionDoesntOccur()Sie wahrscheinlich zu viel in einem Test.

In Ihrem Fall denke ich, dass es wahrscheinlich in Ordnung ist, dreimal zu behaupten. Wenn Sie Ihren Code lesbarer machen möchten, können Sie diese drei Asserts in eine Methode mit dem Namen extrahieren assertWasCalledOnTargets(...).

Daniel Kaplan
quelle
3

Für Ihr spezielles Beispiel können Sie mit "einer" Assert-Anweisung davonkommen, wenn Sie Folgendes tun:

foreach target in targets
{
     rule1.AssertWasCalled(fnord => fnord.Test(target))
}

Es ist das, was ich tue, um zu vermeiden, dass ich mich schuldig fühle, wenn ich mehrere Behauptungen in einem Test habe.

Jeff B.
quelle
Ich habe das schon mal gemacht. Kein schlechter Weg. Es ist leicht zu lesen und Sie können verstehen, was es tut.
CokoBWare
1

Ich habe auch mit diesem gekämpft.

Der Purist (in mir) besteht auf einer Behauptung pro Test, damit ich * genau * weiß , wo die Dinge explodierten.

Und dann schneide / füge ich viel des gleichen redundanten Test-Setup-Codes ein. Nach der dritten oder vierten Schicht beginnen Sie zu sagen "Oy! Genug!"

Mein Kompromiss bestand darin, die Aspekte zu finden, die "nie" brechen. Und ich werde diese Teile zusammenfügen und dann ein neues Element hinzufügen , das brechen könnte. Um ganz klar zu sein, würde die Schichtung mehrerer flüchtiger Bereiche in einem Test einen Verstoß gegen diesen Kompromiss darstellen.


quelle
1
Sie sollten auschecken Assume. Ich habe es heute gerade erfahren.
Wayne Werner
1

Wenn sich der Setup-Code für target1vom Setup-Code für unterscheidet target2, führt diese Art des Eckenschneidens möglicherweise zu einem zu langen Testinitialisierungscode. Dies wiederum ist entweder ein Chaos oder wird überarbeitet und wiederverwendet. Wenn Ihre Tests komplex genug sind, um eine Umgestaltung zu rechtfertigen, testet Ihr Test wahrscheinlich mehr als eine Sache.

Wenn der Setup-Code für jedes Ziel im Wesentlichen derselbe ist, ist die Aufteilung Ihres Tests in mehrere Einzeltests wahrscheinlich übertrieben.

Wenn target1und target2verschiedene Implementierungen derselben Schnittstelle sind, sollten Sie stattdessen sein Hinzufügen einer Unit - Test an die Schnittstelle (und damit Ihre Test - Framework einen Test für jede Implementierung dieser Schnittstelle erzeugen).

Brian
quelle