Für das Projekt, an dem mein Team und ich arbeiten, stellen wir häufig fest, dass wir große Teile des Gerüstcodes benötigen. Das Erstellen von Domänenobjekten mit korrekten Werten, das Einrichten von Mocks für Repositorys, der Umgang mit dem Cache usw. sind alles Dinge, die während der Tests häufig vorkommen. Oft arbeiten wir mit denselben Basisobjekten, die für unsere Domäne von zentraler Bedeutung sind (Person, ...), sodass bei vielen Tests Instanzen dieser Objekte erstellt werden, an denen andere Objekte arbeiten können. Wir haben viele verschiedene Lösungen, die die Basisdomäne verwenden, daher wird diese Art von Code häufig auf diese Lösungen verteilt.
Ich habe darüber nachgedacht, gemeinsame Klassen zu schaffen, die einen Großteil dieses Gerüsts übernehmen. Dies würde es uns ermöglichen, eine vollständig instanziierte Person mit allem, was eingerichtet ist (Zugriff über Repository, Caching ...), anzufordern. Dies entfernt doppelten Code aus unseren einzelnen Komponententests, würde aber auch bedeuten, dass es eine große Menge an Code gibt, die wahrscheinlich "zu viel" pro Test ausführt (da dies ein vollständiges Objekt und nicht nur die erforderlichen Teile einrichten würde).
Hat das jemals jemand gemacht? Gibt es Erkenntnisse, Bemerkungen, Gedanken ..., die Sie anbieten können, um diesen Ansatz zu bestätigen oder ungültig zu machen?
quelle
Antworten:
Ich verwende das Builder-Muster, um Domänenobjekte für Tests zu erstellen, wie im Buch " Wachsende objektorientierte Software " beschrieben. Die Builder verfügen über statische Methoden, die Standardwerte für allgemeine Objekte mit Methoden zum Überschreiben der Standardwerte generieren.
Sie können diese kombinieren, um komplexere Objekte zu generieren.
Wir verwenden auch die Vererbung von Testfallklassen mit häufig verwendeten Mocks für bestimmte Arten von Komponententests. Beispielsweise erfordern MVC-Controller-Tests häufig dieselben Mocks / Stubs für Anforderungs- und Antwortobjekte.
quelle
Das Einrichten für alle Tests kann eine gute Idee sein, insbesondere wenn Sie eine Bereitstellung durchführen. Anstatt Mocks zu verwenden, erstellen, implementieren und füllen wir eine Testdatenbank und richten den Test dann auf die Instanz. Nicht genau so, wie Sie es gemäß dem Handbuch tun sollten, aber es funktioniert, umso mehr, als wir unseren Bereitstellungscode verwenden, um dies zu tun.
quelle
Bedwyr Humphreys spricht von einer Builder-Lösung, die ich einige Male verwendet habe und die wirklich gut funktioniert. Fast keine Codeduplizierung und keine saubere Erstellung von Domänenobjekten.
Ich bin kein Fan der Codegenerierung (Gerüste, Codesmith, lblgen, ..). Es hilft Ihnen nur, doppelten Code viel schneller und nicht wirklich wartungsfreundlich zu schreiben. Wenn ein Framework dazu führt, dass Sie viel doppelten (Test-) Code schreiben, müssen Sie möglicherweise einen Weg finden, um die Codebasis vom Framework zu entfernen.
Wenn Sie weiterhin Repository-Zugangscode schreiben, können Sie dies möglicherweise mit einem generischen Basis-Repository lösen und über eine generische Basis-Testklasse verfügen, von der Ihre Komponententests erben.
Um Codeduplizierungen zu vermeiden, kann AOP auch bei der Protokollierung, Zwischenspeicherung, ...
quelle
Möglicherweise möchten Sie das Design des zu testenden Codes überdenken. Vielleicht braucht es einige Beispiele für das Fassadenmuster? Ihr Testcode könnte die Fassaden verwenden und wäre einfacher.
quelle
Ich kann mich auf diese Frage beziehen. In meinem Team haben wir schon früher versucht, TDD zu machen, und es war die gleiche Geschichte. Zu viele Tests erforderten viele Gerüst- / Dienstprogrammmethoden, die wir einfach nicht hatten. Aufgrund des Zeitdrucks und einiger anderer nicht so richtiger Entscheidungen endeten wir mit Unit-Tests, die sehr lang waren und die Dinge neu erfanden. Im Laufe einer Veröffentlichung wurden diese Unit-Tests so kompliziert und schwer zu warten, dass wir sie fallen lassen mussten.
Ich arbeite derzeit mit wenigen anderen Entwicklern an der Wiedereinführung von TDD in das Team, aber anstatt den Leuten einfach zu sagen, Tests zu schreiben, möchten wir diesmal vorsichtig vorgehen und die Leute haben die richtigen Fähigkeiten und die richtigen Werkzeuge. Das Fehlen des allgemeinen Support-Codes ist eine Sache, die wir identifiziert haben, wo wir uns verbessern müssen, damit der gesamte Code, den sie schreiben, klein und auf den Punkt kommt, wenn andere anfangen, Tests zu schreiben.
Ich habe noch nicht so viel Erfahrung in diesem Bereich, wie ich möchte, aber einige Dinge, die ich vorschlagen würde, basierend auf unserer bisherigen Arbeit und einigen Hausaufgaben, die ich gemacht habe:
quelle
"Jedes Problem in der Informatik kann mit einer zusätzlichen Indirektionsebene gelöst werden. Aber das schafft normalerweise ein anderes Problem." - David Wheeler
(Haftungsausschluss: Ich bin Teil des JDT-Teams, daher habe ich mehr inneres Wissen über das hier vorgestellte Problem.)
Das wesentliche Problem hierbei ist, dass die Objekte im Framework selbst eine Datenbankverbindung erwarten, da diese lose auf dem CSLA-Framework von Rockford Lhotka basiert. Dies macht es fast unmöglich, sie zu verspotten (da Sie das Framework ändern müssten, um dies zu unterstützen) und verstößt auch gegen das Black-Box-Prinzip, das ein Unit-Test haben sollte.
Ihre vorgeschlagene Lösung ist zwar an sich keine schlechte Idee, erfordert jedoch nicht nur eine Menge Arbeit, um zu einer stabilen Lösung zu gelangen, sondern fügt auch eine weitere Ebene von Klassen hinzu, die gewartet und erweitert werden müssen, wodurch eine bereits vorhandene noch komplexer wird komplexe Situation.
Ich stimme zu, dass Sie immer nach der besten Lösung suchen sollten, aber in einer realen Situation wie dieser hat das Praktische auch seine Werte.
Und die Praktikabilität schlägt die folgende Option vor:
Verwenden Sie eine Datenbank.
Ja, ich weiß, dass es technisch gesehen kein "echter" Unit-Test mehr ist, aber genau genommen ist es auch kein realer Unit-Test mehr, sobald Sie in Ihrem Test eine Klassengrenze überschreiten. Was wir uns hier fragen müssen, ist, ob wir "reine" Komponententests wollen, die eine Menge Gerüst- und Klebercode erfordern, oder ob wir testbaren Code wollen ?
Sie können auch die Tatsache nutzen, dass das verwendete Framework den gesamten Datenbankzugriff für Sie kapselt, und Ihre Tests für eine saubere Sandbox-Datenbank ausführen. Wenn Sie dann jeden Test in einer eigenen Transaktion ausführen und am Ende jedes 'logischen' Satzes von Tests einen Rollback durchführen, sollten Sie keine Probleme mit der gegenseitigen Abhängigkeit oder der Testreihenfolge haben.
Versuchen Sie nicht, eine Datenbank aus der Datenbank zu entfernen.
quelle
Ich würde davor warnen, zu viel Code zu haben, der zu viel "schwarze Magie" bewirkt, ohne dass Entwickler wissen, was genau eingerichtet wird. Mit der Zeit werden Sie auch sehr abhängig von diesem Code / diesen Daten. Deshalb bin ich kein Fan davon, Ihre Tests auf Testdatenbanken zu stützen. Ich glaube, Testdatenbanken dienen zum Testen als Benutzer, nicht für automatisierte Tests.
Das heißt, Ihr Problem ist real. Ich würde einige gängige Methoden entwickeln, um Daten einzurichten, die Sie regelmäßig benötigen. Versuchen Sie im Idealfall, die Methoden zu parametrisieren, da bestimmte Tests unterschiedliche Daten benötigen.
Aber ich würde es vermeiden, große Objektgraphen zu erstellen. Am besten lassen Sie Tests mit dem Minimum an Daten ausführen, die sie benötigen. Wenn viele andere Daten vorhanden sind, können Sie Ihre Testergebnisse unerwartet beeinflussen (Worst-Case-Szenario).
Ich mag es, meine Tests so "silo'ed" wie möglich zu halten. Es ist jedoch eine subjektive Frage. Aber ich würde mich für gängige Methoden mit Parametern entscheiden, damit Sie Ihre Testdaten in nur wenigen Codezeilen einrichten können.
quelle
Alle Möglichkeiten in den Antworten zusammengefasst:
Verwenden Sie das Builder-Muster (Bedwyr Humphreys, DXM und Pascal Mestdach).
Vorteile:
Nachteile:
Verwenden Sie eine Testdatenbank (Tony Hopkinson und Sam)
Vorteile:
Nachteile:
Erstellen Sie Domänenobjekte pro Test (Peter)
Vorteile:
Nachteile:
Verwenden Sie Fassaden für Domänenobjekte (Raedwald)
Vorteile:
Nachteile:
quelle