Wie halten Sie Ihre Komponententests einfach und isoliert und garantieren trotzdem DDD-Invarianten?

8

DDD empfiehlt, dass die Domänenobjekte jederzeit in einem gültigen Zustand sein sollten. Aggregatwurzeln sind dafür verantwortlich, die Invarianten und Fabriken für das Zusammenstellen von Objekten mit allen erforderlichen Teilen zu garantieren, damit sie in einem gültigen Zustand initialisiert werden.

Allerdings scheint dies die Aufgabe zu erschweren, einfache, isolierte Unit-Tests zu erstellen.

Nehmen wir an, wir haben ein BookRepository, das Bücher enthält. Ein Buch hat:

  • ein Autor
  • eine Kategorie
  • Eine Liste von Buchhandlungen finden Sie in

Dies sind die erforderlichen Attribute: Ein Buch muss einen Autor, eine Kategorie und mindestens einen Buchladen haben, von dem Sie das Buch kaufen können. Es gibt wahrscheinlich eine BookFactory, da es ein ziemlich komplexes Objekt ist und die Factory das Buch mit mindestens allen genannten Attributen initialisiert. Vielleicht machen wir den Buch-Konstruktor auch privat (und die Fabrik verschachtelt), so dass niemand ein leeres Buch außer der Fabrik instanziieren kann.

Nun wollen wir eine Methode des BookRepository testen, die alle Bücher zurückgibt. Um zu testen, ob die Methode die Bücher zurückgibt, müssen wir einen Testkontext (den AAA-Anordnungsschritt) einrichten, in dem sich einige Bücher bereits im Repository befinden.

In C #:

%Vor%

Da das einzige Werkzeug, das uns zur Verfügung steht, um Bücherobjekte zu erstellen, die Factory ist, verwendet der Komponententest nun Factory und unabhängig von Kategorie, Autor und Store, da wir diese Objekte benötigen, um ein Buch zu erstellen und dann Platziere es im Testkontext.

Würdest du davon ausgehen, dass es sich um eine Abhängigkeit handelt, so wie in einem Service-Unit-Test wir zum Beispiel von einem Repository abhängen würden, das der Service aufrufen würde?

Wie würden Sie das Problem lösen, dass Sie einen ganzen Cluster von Objekten neu erstellen müssen, um eine einfache Sache testen zu können? Wie würdest du diese Abhängigkeit brechen und all diese Buchattribute loswerden, die wir in unserem Test nicht brauchen? Mit Mocks oder Stubs?

Wenn Sie Dinge nachbilden, die ein Repository enthält , welche Art von Mock / Stubs würden Sie verwenden, anstatt wenn Sie etwas munkeln, spricht das zu testende Objekt mit oder verbraucht ?

    
guillaume31 14.05.2010, 10:33
quelle

6 Antworten

4

Zwei Dinge:

  • Verwenden Sie Mock-Objekte innerhalb der Tests. Sie verwenden derzeit konkrete Objekte.

  • Im Hinblick auf die Einrichtung des Komplexes benötigen Sie irgendwann einige gültige Bücher. Extrahieren Sie diese Logik in eine Einrichtungsmethode, die vor jedem Test ausgeführt wird. Lassen Sie diese Methode eine gültige Sammlung von Büchern erstellen und so weiter.

  

"Wie würdest du das Problem lösen?   müssen einen ganzen Cluster von   Objekte, um a   einfache Sache ? Wie würdest du brechen?   diese Abhängigkeit und loswerden von allem   Diese Buchattribute brauchen wir nicht   unser Test? Mit Mocks oder Stubs? "

Mit einem Mock-Objekt können Sie dies tun. Wenn ein Test nur ein Buch mit einem gültigen Autor benötigt, würde Ihr Mock-Objekt diesen Autor angeben, die anderen Attribute wären voreingestellt. Da sich Ihr Test nur um einen gültigen Autor kümmert, müssen die anderen Attribute nicht eingerichtet werden.

    
Finglas 14.05.2010 12:45
quelle
3

Für reine Unit-Tests sind Mocks und Stubs definitiv die Lösung. Aber da Sie nach einer mehr Integration Ebene Tests gehen und Mocks (oder Stubs oder was auch immer) Ihr Problem nicht lösen, haben Sie wirklich zwei vernünftige Möglichkeiten:

  • Erstellen Sie Testfactorys , um Ihnen beim Einrichten der benötigten Daten zu helfen. Diese werden wahrscheinlich test-spezifisch sein, die nicht nur einen Buchladen aufbauen, sondern ihn mit einem vernünftigen Set-up-Buch füllen. Auf diese Weise komprimieren Sie Ihren Setup-Code in eine oder zwei Zeilen und verwenden sie für andere Tests. Dieser Code kann wachsen, um verschiedene Szenarien zu erstellen, die für Integrationstests benötigt werden.

  • Erstellen Sie eine Einrichtung Test Fixtures . Dies sind kleine, aber konzeptionell vollständige Datensätze für Ihre Tests. Diese werden im Allgemeinen in einer Art serialisierter Form (xml, csv, sql) gespeichert und zu Beginn jedes Tests in Ihre Datenbank geladen, so dass Sie einen gültigen Status haben. Sie sind wirklich nur eine allgemeine Fabrik, die statische Dateien liest.

Wenn Sie Fixtures verwenden, können Sie den Ansatz für einzelne oder mehrere Fixtures verwenden. Wenn Sie für die meisten Ihrer Komponententests mit einer einzigen "kanonischen" Menge von Daten durchkommen können, ist das einfacher, aber manchmal wird dadurch ein Datensatz erstellt, der zu viele Datensätze enthält, um verständlich zu sein, oder einfach den Bereich nicht ausdrückt von Szenarien, die Sie unterstützen müssen. Bei einigen Problemen müssen mehrere Datensätze gründlich getestet werden.

    
ndp 17.05.2010 15:00
quelle
1

Danke Finglas für die Antwort. Ich verwende Mocks in anderen Tests, aber hauptsächlich für Interaktionstests, nicht zum Einrichten des Testkontextes. Ich war mir nicht sicher, ob diese Art von Hohlkörper mit genau den erforderlichen Werten als Spott bezeichnet werden könnte und ob es eine gute Idee war, sie zu verwenden.

Ich habe bei Gerard Meszaros 'xunitpatterns.com etwas Interessantes gefunden, das ziemlich nah am Problem ist. Er beschreibt den Code-Geruch eines langen und komplizierten Test-Setups als Irrelevante Informationen , wobei mögliche Lösungen Erstellungsmethoden oder Dummy-Objekte . Ich bin jedoch nicht vollständig auf seine Dummy-Objekt-Implementierung verkauft, da in meinem Beispiel es mich zwingen würde, eine IBook-Schnittstelle (UGH) zu haben, um ein Dummy-Buch mit einem sehr einfachen Konstruktor zu implementieren und die gesamte Factory-Erstellungslogik zu umgehen.

Ich denke, eine Mischung aus Isolations-Framework-generierten Mocks und Erstellungsmethoden könnte mir helfen, meine Tests zu klären und zu vereinfachen.

    
guillaume31 14.05.2010 15:13
quelle
1

Vielleicht möchten Sie einen Testdatengenerator ausprobieren. Netter Beitrag von Nat Pryce .

Dies kann helfen, wenn Sie nicht die Route von Mocks gehen wollen. Es kann all diese hässlichen Fabrikmethoden abstrahieren. Sie können auch versuchen, die Builder in Ihrem Produktionscode zu verwenden.

    
Gutzofter 14.05.2010 16:38
quelle
1
  

Vielleicht machen wir auch das Buch   Konstruktor privat (und die Factory   verschachtelt), so dass niemand instanziieren kann   ein leeres Buch außer der Fabrik.

Der private Book -Konstruktor ist die Quelle Ihrer Probleme.

Wenn Sie stattdessen den Konstruktor " Book " internieren, muss die Factory nicht geschachtelt werden. Dann können Sie die Fabrik dazu bringen, eine Schnittstelle zu implementieren ( IBookFactory ), und Sie können eine Mock-Book-Factory in Ihr Repository einfügen.

Wenn Sie wirklich sicherstellen wollen, dass nur Factory-Implementierungen Instanzen erstellen, fügen Sie Ihrem Repository eine Methode hinzu, die die Argumente akzeptiert, die das Werk benötigt:

%Vor%     
Jeff Sternal 17.05.2010 15:05
quelle
0

II könnte voreingenommen sein, weil ich angefangen habe, DDD neben CQRS zu lernen. Aber ich bin mir nicht sicher, ob du die richtigen Grenzen gezogen hast. Ein Aggregat sollte nur über seine Invarianten Bescheid wissen. Du sagst, ein Buch hat einen Autor. Ja, aber das Buch hat keine Invariante auf den Namen des Autors. so könnten wir uns das Gesamtbuch wie folgt vorstellen:

%Vor%

Während der Autor eine Invariante auf seinen Autor hat:

%Vor%

Die Abfrageseite würde zwar sowohl den Namen des Informationsbuchs als auch den Namen des Autors benötigen, aber dies ist eine Abfrage und möglicherweise nicht für Unit-Tests von IMO geeignet.

Wenn Sie in der Lage sein müssen, zu Ihrer Bibliothek hinzuzufügen, nur buchen, wenn ihr Autor den Buchstaben "e" hat, dann ist die ganze Diskussion anders, aber von dem, was ich verstanden habe, brauchen Sie es jetzt nicht.

Wenn Sie das Aggregat Book erstellen, wird Ihr Komponententest einfacher, weil Sie sich auf die Schreibseite und die wahren Invarianten konzentrieren.

    
Arthis 02.01.2013 15:06
quelle