Ich habe eine Klasse, die in 1 Hauptklasse und 2 kleineren Klassen überarbeitet wird. Die Hauptklassen verwenden die Datenbank (wie viele meiner Klassen) und senden eine E-Mail. Also hat die Hauptklasse ein IPersonRepository
und ein IEmailRepository
gespritzt was seinerseits an die 2 kleineren Klassen schickt.
Jetzt möchte ich die Hauptklasse einem Unit-Test unterziehen und habe gelernt, die internen Abläufe der Klasse nicht zu zerstören, da wir in der Lage sein sollten, die internen Abläufe zu ändern, ohne die Unit-Tests zu unterbrechen.
Aber da die Klasse die verwendet IPersonRepository
und eine IEmailRepository
, ich HABE angeben (Mock / Dummy) Ergebnisse für einige Methoden für die IPersonRepository
. Die Hauptklasse berechnet einige Daten basierend auf vorhandenen Daten und gibt diese zurück. Wenn ich das testen möchte, sehe ich nicht, wie ich einen Test schreiben kann, ohne anzugeben, dass das IPersonRepository.GetSavingsByCustomerId
x zurückgibt. Aber dann "weiß" mein Unit-Test über die internen Abläufe Bescheid, weil er "weiß", welche Methoden zu verspotten sind und welche nicht.
Wie kann ich eine Klasse testen, die Abhängigkeiten eingeführt hat, ohne dass der Test über die Interna Bescheid weiß?
Hintergrund:
Nach meiner Erfahrung erstellen viele Tests wie diese Mocks für die Repositorys und stellen dann entweder die richtigen Daten für die Mocks bereit oder testen, ob während der Ausführung eine bestimmte Methode aufgerufen wurde. In jedem Fall kennt der Test die Interna.
Jetzt habe ich eine Präsentation über die Theorie gesehen (die ich zuvor gehört habe), dass der Test nichts über die Implementierung wissen sollte. Erstens, weil Sie nicht testen, wie es funktioniert, sondern auch, weil, wenn Sie jetzt die Implementierung ändern, alle Komponententests fehlschlagen, weil sie über die Implementierung Bescheid wissen. Obwohl mir das Konzept der Tests nicht bekannt ist, weiß ich nicht, wie ich es durchführen soll.
quelle
IPersonRepository
Objekt erwartet , sind diese Schnittstelle und alle darin beschriebenen Methoden nicht mehr "intern", sodass es sich nicht wirklich um ein Testproblem handelt. Ihre eigentliche Frage sollte lauten: "Wie kann ich Klassen in kleinere Einheiten umgestalten, ohne zu viel in der Öffentlichkeit preiszugeben?" Die Antwort lautet "Halten Sie diese Schnittstellen schlank" (indem Sie sich beispielsweise an das Prinzip der Schnittstellenseggregation halten). Das ist meiner Meinung nach Punkt 2 in der Antwort von @ DavidArno (ich glaube, ich muss das nicht in einer anderen Antwort wiederholen).Antworten:
Sie haben Recht, dass dies eine Verletzung des Prinzips "Interna nicht testen" ist und häufig übersehen wird.
Es gibt zwei Lösungen, mit denen Sie diesen Verstoß umgehen können:
1) Geben Sie ein vollständiges Modell von an
IPersonRepository
. Ihr derzeit beschriebener Ansatz besteht darin, den Mock an die inneren Abläufe der zu testenden Methode zu koppeln, indem Sie nur die aufgerufenen Methoden verspotten. Wenn Sie Mocks für alle Methoden vonIPersonRepository
angeben, entfernen Sie diese Kupplung. Das Innenleben kann sich ändern, ohne den Schein zu beeinträchtigen, wodurch der Test weniger spröde wird.Dieser Ansatz hat den Vorteil, dass der DI-Mechanismus einfach gehalten wird. Er kann jedoch viel Arbeit verursachen, wenn Ihre Schnittstelle viele Methoden definiert.
2) Spritzen Sie
IPersonRepository
weder dieGetSavingsByCustomerId
Methode noch einen Einsparungswert ein. Das Problem beim Injizieren ganzer Schnittstellenimplementierungen besteht darin, dass Sie dann ein "Ask, Don't Tell" -System injizieren ("Tell, Don't Ask") und die beiden Ansätze vertauschen. Wenn Sie einen "reinen DI" -Ansatz wählen, sollte der Methode die genaue aufzurufende Methode bereitgestellt (mitgeteilt) werden, wenn ein Einsparungswert gewünscht wird, anstatt ein Objekt zu erhalten (das dann effektiv abgefragt werden muss, damit die Methode aufgerufen wird).Der Vorteil dieses Ansatzes besteht darin, dass keine Verspottungen erforderlich sind (über die Testmethoden hinaus, die Sie in die zu testende Methode einfügen). Der Nachteil besteht darin, dass sich die Methodensignaturen ändern können, wenn sich die Anforderungen der Methode ändern.
Beide Ansätze haben ihre Vor- und Nachteile. Wählen Sie also den Ansatz, der Ihren Anforderungen am besten entspricht.
quelle
Mein Ansatz ist es, Scheinversionen der Repositorys zu erstellen, die aus einfachen Dateien mit den erforderlichen Daten gelesen werden.
Dies bedeutet, dass der einzelne Test nichts über das Setup des Mocks 'weiß', obwohl das gesamte Testprojekt offensichtlich auf das Mock verweist und die Setup-Dateien usw. enthält.
Dies vermeidet die komplexe Einrichtung von Scheinobjekten, die für das Verspotten von Frameworks erforderlich ist, und ermöglicht es Ihnen, die Scheinobjekte in realen Instanzen Ihrer Anwendung für UI-Tests und dergleichen zu verwenden.
Da der Mock vollständig implementiert ist und nicht speziell für Ihr Testszenario eingerichtet wurde, sollte bei einer Änderung der Implementierung beispielsweise GetSavingsForCustomer jetzt auch den Kunden löschen. Die Tests werden nicht abgebrochen (es sei denn, es werden die Tests wirklich abgebrochen). Sie müssen nur Ihre einzelne Mock-Implementierung aktualisieren, und alle Ihre Tests werden dagegen ausgeführt, ohne dass ihre Einrichtung geändert wird
quelle
Unit-Tests sind in der Regel Whitebox-Tests (Sie haben Zugriff auf den realen Code). Daher ist es in Ordnung, Interna bis zu einem gewissen Grad zu kennen, für Anfänger ist es jedoch einfacher, dies nicht zu tun, da Sie Interna nicht testen sollten Verhalten (z. B. "Methode a zuerst, dann b und dann a erneut aufrufen").
Das Injizieren eines Modells, das Daten bereitstellt, ist in Ordnung, da Ihre Klasse (Einheit) von diesen externen Daten (oder dem Datenanbieter) abhängt. Sie sollten jedoch die Ergebnisse überprüfen (nicht den Weg, um zu ihnen zu gelangen)! Sie stellen z. B. eine Personeninstanz bereit und stellen sicher, dass eine E-Mail an die richtige E-Mail-Adresse gesendet wurde, indem Sie z. B. auch einen Schein für die E-Mail bereitstellen. Dieser Schein speichert lediglich die E-Mail-Adresse des Empfängers für den späteren Zugriff durch Ihren Test -Code. (Ich glaube, Martin Fowler nennt sie eher Stubs als Mocks.)
quelle