Manchmal müssen Objekte nur eng miteinander verbunden werden. Beispielsweise muss eine CsvFile
Klasse wahrscheinlich eng mit der CsvRecord
Klasse (oder ICsvRecord
Schnittstelle) zusammenarbeiten.
Nach dem, was ich in der Vergangenheit gelernt habe, lautet einer der wichtigsten Grundsätze der testgetriebenen Entwicklung: "Testen Sie niemals mehr als eine Klasse gleichzeitig." Das heißt, Sie sollten ICsvRecord
Mocks oder Stubs anstelle von tatsächlichen Instanzen von verwenden CsvRecord
.
Nachdem ich diesen Ansatz ausprobiert hatte, bemerkte ich jedoch, dass das Verspotten der CsvRecord
Klasse etwas haarig werden kann. Was mich zu einer von zwei Schlussfolgerungen führt:
- Es ist schwer, Unit-Tests zu schreiben! Das ist ein Code-Geruch! Refactor!
- Es ist einfach unvernünftig, jede einzelne Abhängigkeit auszuspotten.
Als ich meine Verspottungen durch tatsächliche CsvRecord
Instanzen ersetzte , lief es viel reibungsloser. Bei der Suche um für andere Völker Gedanken stolperte ich über diese Blog - Post , die oben # 2 zu unterstützen scheint. Bei Objekten, die von Natur aus eng miteinander verbunden sind, sollten wir uns nicht so sehr um das Verspotten kümmern.
Bin ich weit weg von der Strecke? Gibt es irgendwelche Nachteile bei der obigen Annahme Nr. 2? Sollte ich tatsächlich darüber nachdenken, mein Design umzugestalten?
Antworten:
Wenn Sie wirklich eine Koordination zwischen diesen beiden Klassen benötigen, schreiben Sie eine
CsvCoordinator
Klasse, die Ihre beiden Klassen kapselt, und testen Sie diese.Ich bestreite jedoch den Begriff, der
CsvRecord
nicht unabhängig überprüfbar ist.CsvRecord
ist im Grunde eine DTO- Klasse, nicht wahr ? Es ist nur eine Sammlung von Feldern mit vielleicht ein paar Hilfsmethoden. UndCsvRecord
kann auch in anderen Kontexten verwendet werdenCsvFile
; Sie können beispielsweise eine Sammlung oder ein Array vonCsvRecord
s haben.CsvRecord
Zuerst testen . Stellen Sie sicher, dass alle Tests bestanden wurden. Dann mach weiter und benutze esCsvRecord
mit deinerCsvFile
Klasse während des Tests. Verwenden Sie es als vorab getesteten Stub / Mock. Füllen Sie es mit relevanten Testdaten, geben Sie es weiterCsvFile
und schreiben Sie Ihre Testfälle dagegen.quelle
CsvRecord
Pausen, dannCsvData
scheitert offensichtlich ; Dies ist jedoch in Ordnung, da SieCsvRecord
zuerst testen und wenn dies fehlschlägt, sind IhreCsvFile
Tests bedeutungslos. Sie können immer noch zwischen Fehlern inCsvRecord
und in unterscheidenCsvFile
.Der Grund für das Testen einer Klasse zu einem Zeitpunkt ist, dass Sie nicht möchten, dass Tests für eine Klasse Abhängigkeiten vom Verhalten einer zweiten Klasse haben. Das heißt, wenn Ihr Test für Klasse A eine der Funktionen von Klasse B ausübt, sollten Sie Klasse B verspotten, um die Abhängigkeit von bestimmten Funktionen in Klasse B zu beseitigen.
Eine Klasse wie
CsvRecord
scheint mir hauptsächlich zum Speichern von Daten zu dienen - es ist keine Klasse mit zu viel eigener Funktionalität. Das heißt, es kann Konstruktoren, Getter, Setter haben, aber keine Methoden mit wirklich substanzieller Logik. Natürlich vermute ich hier - vielleicht haben Sie eine Klasse namens geschriebenCsvRecord
, die zahlreiche komplexe Berechnungen durchführt.Aber wenn
CsvRecord
es keine eigene Logik gibt, kann man nichts gewinnen, wenn man sich darüber lustig macht. Dies ist wirklich nur die alte Maxime - "verspotten Sie keine Wertobjekte" .Wenn Sie also überlegen, ob Sie eine bestimmte Klasse verspotten möchten (für einen Test einer anderen Klasse), sollten Sie berücksichtigen, wie viel von ihrer eigenen Logik diese Klasse hat und wie viel von dieser Logik im Verlauf Ihres Tests ausgeführt wird.
quelle
Nr. 2 ist in Ordnung. Dinge können und sollten eng miteinander verbunden sein, wenn ihre Konzepte eng miteinander verbunden sind. Dies sollte selten sein und im Allgemeinen vermieden werden. In dem von Ihnen angegebenen Beispiel ist dies jedoch sinnvoll.
quelle
"Gekoppelte" Klassen sind voneinander abhängig. Dies sollte in dem, was Sie beschreiben, nicht der Fall sein - ein CsvRecord sollte sich nicht wirklich um die CsvFile kümmern, die es enthält, daher geht die Abhängigkeit nur in eine Richtung. Das ist in Ordnung und keine enge Kopplung.
Wenn eine Klasse den variablen String-Namen enthält, würden Sie doch nicht behaupten, dass sie eng mit String verbunden ist, oder?
Testen Sie den CsvRecord also auf das gewünschte Verhalten.
Verwenden Sie dann ein Verspottungs-Framework (Mockito ist großartig), um zu testen, ob Ihre Einheit mit den Objekten interagiert, von denen es richtig abhängt. Das Verhalten, das Sie wirklich testen möchten, ist, dass CsvFile CsvRcords wie erwartet behandelt. Das Innenleben von CvsRecord sollte keine Rolle spielen - so kommuniziert CvsFile damit.
Schließlich geht es bei TDD nicht nur um Unit-Tests. Sie können (und sollten) sicherlich mit Funktionstests beginnen, die das Funktionsverhalten Ihrer größeren Komponenten untersuchen, dh Ihre User Story oder Ihr Szenario. Ihre Unit-Tests setzen die Erwartungen und verifizieren die Teile, die Funktionstests machen das Gleiche für das Ganze.
quelle
CsvFile
ist eng gekoppeltCsvRecord
(aber nicht umgekehrt). Das OP fragt, ob es eine gute Idee ist, sie zu testen,CsvFile
indem Sie sieCsvRecord
über eine entkoppelnICsvRecord
, nicht umgekehrt.CsvFile
das InnenlebenCsvRecord
, dh die Anzahl der Annahmen, die die Datei über den Datensatz hat, abhängt . Schnittstellen helfen dabei, solche Annahmen zu dokumentieren und durchzusetzen (oder vielmehr das Fehlen anderer Annahmen), aber der Umfang der Kopplung bleibt gleich, außer dass Sie mit einer Schnittstelle eine andere Datensatzklasse einbinden könnenCsvFile
. Die Einführung der Schnittstelle, nur damit Sie sagen können, dass Sie die Kopplung reduziert haben, ist dumm.Hier gibt es wirklich zwei Fragen. Das erste ist, wenn Situationen existieren, in denen das Verspotten eines Objekts nicht ratsam ist. Das ist zweifellos wahr, wie die anderen hervorragenden Antworten zeigen. Die zweite Frage ist, ob Ihr spezieller Fall eine dieser Situationen ist. In dieser Frage bin ich nicht überzeugt.
Der wahrscheinlich häufigste Grund, eine Klasse nicht zu verspotten, ist, wenn es sich um eine Wertklasse handelt. Sie müssen sich jedoch den Grund für die Regel ansehen. Das liegt nicht daran, dass die verspottete Klasse irgendwie schlecht sein wird, sondern daran, dass sie im Wesentlichen mit dem Original identisch ist. Wenn dies der Fall wäre, wäre Ihr Komponententest mit der ursprünglichen Klasse nicht einfacher.
Es kann sehr gut sein, dass Ihr Code eine der seltenen Ausnahmen ist, bei denen Refactoring nicht helfen würde, aber Sie sollten ihn nur dann deklarieren, wenn sorgfältige Refactoring-Bemühungen nicht funktioniert haben. Selbst erfahrene Entwickler können Probleme haben, Alternativen zu ihrem eigenen Design zu finden. Wenn Ihnen keine Möglichkeit zur Verbesserung einfällt, bitten Sie einen erfahrenen Mitarbeiter, einen zweiten Blick darauf zu werfen.
Die meisten Leute scheinen anzunehmen, dass Sie
CsvRecord
eine Werteklasse sind. Versuchen Sie es zu einem. Mach es unveränderlich, wenn du kannst. Wenn Sie zwei Objekte mit Zeigern aufeinander haben, entfernen Sie eines davon und finden Sie heraus, wie es funktioniert. Suchen Sie nach Orten, an denen Klassen und Funktionen aufgeteilt werden können. Der beste Ort zum Teilen einer Klasse stimmt möglicherweise nicht immer mit dem physischen Layout der Datei überein. Versuchen Sie, die Eltern-Kind-Beziehung der Klassen umzukehren. Möglicherweise benötigen Sie eine separate Klasse zum Lesen und Schreiben von CSV-Dateien. Möglicherweise benötigen Sie separate Klassen für die Verarbeitung der Datei-E / A und der Schnittstelle zu den oberen Ebenen. Es gibt viele Dinge zu versuchen, bevor es für unrefaktable erklärt wird.quelle