In seinem Vortrag TDD, in dem alles schief gelaufen ist , drängt Ian Cooper Kent Becks ursprüngliche Absicht hinter Unit-Tests in TDD (um Verhaltensweisen zu testen, nicht Methoden für Klassen) und argumentiert, die Tests nicht an die Implementierung zu koppeln.
save X to some data source
Wie können wir bei einem Verhalten wie in einem System mit einem typischen Satz von Diensten und Repositorys das Speichern einiger Daten auf Serviceebene über das Repository testen, ohne den Test an Implementierungsdetails zu koppeln (z. B. das Aufrufen einer bestimmten Methode)? )? Lohnt es sich nicht, diese Art der Kopplung zu vermeiden?
unit-testing
tdd
coupling
Andy Hunt
quelle
quelle
Antworten:
Ihr spezielles Beispiel ist ein Fall, den Sie normalerweise testen müssen, indem Sie überprüfen, ob eine bestimmte Methode aufgerufen wurde, da dies
saving X to data source
bedeutet , dass Sie mit einer externen Abhängigkeit kommunizieren. Das zu testende Verhalten besteht also darin, dass die Kommunikation wie erwartet erfolgt .Dies ist jedoch keine schlechte Sache. Die Grenzflächen zwischen Ihrer Anwendung und ihren externen Abhängigkeiten sind keine Implementierungsdetails , sondern werden in der Architektur Ihres Systems definiert. Dies bedeutet, dass sich eine solche Grenze wahrscheinlich nicht ändert (oder wenn dies erforderlich ist, wäre dies die am wenigsten häufige Änderung). Das Koppeln Ihrer Tests an eine
repository
Schnittstelle sollte Ihnen daher nicht zu viele Probleme bereiten (wenn dies der Fall ist, sollten Sie berücksichtigen, ob die Schnittstelle der Anwendung keine Verantwortlichkeiten stiehlt).Berücksichtigen Sie jetzt nur die Geschäftsregeln einer Anwendung, die von der Benutzeroberfläche, den Datenbanken und anderen externen Diensten entkoppelt sind. Hier müssen Sie die Struktur und das Verhalten des Codes ändern können. Hier werden Sie durch Kopplungstests und Implementierungsdetails gezwungen, mehr Testcode als Produktionscode zu ändern, selbst wenn sich das Gesamtverhalten der Anwendung nicht ändert. Hier können wir testen,
State
anstattInteraction
schneller zu arbeiten.PS: Ich möchte nicht sagen, ob das Testen nach Status oder Interaktionen der einzig wahre Weg für TDD ist. Ich glaube, es geht darum, das richtige Tool für den richtigen Job zu verwenden.
quelle
Meine Interpretation dieses Vortrags lautet:
Es wird im Vortrag nicht angegeben, aber ich denke, der angenommene Kontext für den Rat ist ungefähr so:
Das Testen einer Komponente ist daher der größtmögliche Umfang, in dem etwas noch vernünftigerweise als Komponententest bezeichnet werden kann. Dies unterscheidet sich stark davon, wie manche Menschen, insbesondere Akademiker, den Begriff verwenden. Es ist nichts anderes als die Beispiele im typischen Tutorial für Unit-Test-Tools. Es entspricht jedoch seinem Ursprung beim Testen von Hardware. Platinen und Module sind einheitlich getestet, keine Drähte und Schrauben. Oder zumindest bauen Sie keine Schein-Boeing, um eine Schraube zu testen ...
Daraus zu extrapolieren und einige meiner eigenen Gedanken einzubringen,
Wenn Sie das richtig und sauber machen, brauchen Sie kaum ein Spottwerkzeug. Es wird nur einige Male pro System verwendet.
Eine Datenbank ist im Allgemeinen ein Kollaborateur, daher wird sie eher gefälscht als verspottet. Es wäre schmerzhaft, dies von Hand umzusetzen. Zum Glück gibt es solche Dinge bereits .
Das grundlegende Testmuster besteht darin, eine Abfolge von Vorgängen auszuführen (z. B. Speichern und erneutes Laden eines Dokuments). Bestätigen Sie, dass es funktioniert. Dies ist das gleiche wie für jedes andere Testszenario. Keine (funktionierende) Implementierungsänderung führt wahrscheinlich dazu, dass ein solcher Test fehlschlägt.
Die Ausnahme besteht darin, dass Datenbankeinträge vom zu testenden System geschrieben, aber nie gelesen werden. zB Überwachungsprotokolle oder ähnliches. Dies sind Ausgänge und sollten daher verspottet werden. Das Testmuster besteht aus einer Abfolge von Operationen. Bestätigen Sie, dass die Überwachungsschnittstelle mit den angegebenen Methoden und Argumenten aufgerufen wurde.
Beachten Sie, dass selbst hier, sofern Sie ein typsicheres Verspottungswerkzeug wie mockito verwenden , das Umbenennen einer Schnittstellenmethode keinen Testfehler verursachen kann. Wenn Sie eine IDE mit geladenen Tests verwenden, wird diese zusammen mit der Umbenennung der Methode überarbeitet. Wenn Sie dies nicht tun, wird der Test nicht kompiliert.
quelle
Mein Vorschlag ist, einen zustandsbasierten Testansatz zu verwenden:
GEGEBEN Wir haben die Test-DB in einem bekannten Zustand
WANN Der Dienst wird mit Argumenten X aufgerufen
DANN Stellen Sie sicher, dass die Datenbank von ihrem ursprünglichen Status in den erwarteten Status geändert wurde, indem Sie schreibgeschützte Repository-Methoden aufrufen und ihre zurückgegebenen Werte überprüfen
Auf diese Weise verlassen Sie sich nicht auf einen internen Algorithmus des Dienstes und können dessen Implementierung überarbeiten, ohne die Tests ändern zu müssen.
Die einzige Kopplung besteht hier in dem Dienstmethodenaufruf und den Repository-Aufrufen, die zum Lesen von Daten aus der Datenbank erforderlich sind, was in Ordnung ist.
quelle