# TDD und Mocks - best practice



## Antoras (13. Jul 2011)

Hallo,

ich bräuchte mal euren Rat in Bezug auf das Schreiben von möglichst eleganten Tests, die mit Mock-Objekten arbeiten.

Bisher gehe ich so vor:

Tests (mit Mocks) auf Basis von Schnittstellen schreiben
Testen
Implementierungen der Schnittstellen schreiben
Mock-Code in den Tests löschen/auskommentieren
Nochmal testen

Mir ist das ständige Refactoring des Mock-Codes aber zu aufwendig, lässt sich das irgendwie vermeiden?

Durch DI kann ich zwar erreichen, dass es dem Test egal ist ob er Mocks oder eine Implementierung bekommt, sobald innerhalb des Tests aber mit den Abhängigkeiten kommuniziert werden muss muss ich ja irgendwo das Verhalten der Mocks definieren. Wenn ich das aus dem Test auslagere (und es z.B. zu den Abhängigkeiten setze) hab ich das Problem, dass ich bei etwaigen Änderungen am Test gleich an mehreren Orten den Code editieren muss.
Im Grunde beschränkt sich das Refactoring "nur" auf das Löschen/Auskommentieren des Mock-Verhaltens, bei vielen Tests ist es aber doch nervig.

Wie geht ihr da vor, damit am Schluss möglichst wenig Refactoring benötigt wird?


----------



## schalentier (13. Jul 2011)

Ich versteh nicht, wieso du Mocks (Mock Code) loeschen willst... ?? Mocks sind dazu gedacht, Abhaengigkeiten zu komplexen Klassen "wegzumocken". Das aendert sich nicht, wenn sich irgendwelche Implementierungen aendern. Oder ich versteh grad was falsch.


----------



## maki (13. Jul 2011)

Ich verstehe nicht was du unter Punkt 1 und Punkt 4 verstehst.

Mocks werden nicht "gelöscht" wenn man mit dem test fertig ist, mocks bleben, weil sonst das SUT (Subject under test, also die zu testende klasse/Einheit) nicht mehr isoliert ist. Kann aber verstehen dass dir das löschen von mocks seltsam vorkommt 

Ich mache das ungefähr so:
1. Schreibe einen einfachen test für eine Klasse (ja, kein Interface) die es noch nicht gibt
2. Schreibe den Code den du testen willst, sorge dafür das dein test durchläuft
3. Wenn du merkst, dass dein SUT nun eine Abhängigkeit ("collaborator") haben soll/wird (man will ja SoC einhalten), schreibe ein minimales Interface, und definiere ein mock inkl. verhalten & expectations das dafür sorgt dass dein SUT getestet werden kann (injiziere das mock in das SUT). Das wird übrigens "interface discovery" genannt und ist einer der größten stärken von TDD, damit hilft TDD das Design zu verbessern bzw. zu "entdecken". 
4. Wenn der test durchläuft, refactoring der/des tests und des SUT, dann wieder Punkt 1

Irgendwann wirst du eine Implementierung für das collaborater interface schreiben, bzw. zuerst den test, dann den Code 
Das läuft auch nach diesem Schema, allerdings ist das SUT nun eine Implementierung des "ehemaligen" collaborator.

Man testet niemals mocks, macht gar keinen Sinn, mocks die prüfen ob sie richtig aufgerufen wurden (expectations) sparen einem übrigens viele asserts, kann manchmal sogar tests ganz ohne asserts schreiben, das mock prüft ja schon die expectations.

Es gibt gute Literatur über TDD, zB. das Buch von Beck, aber auch sehr viele gute Internet Artikel, zu Mocks gibt es auch sehr viel, empfehle das den Artikel "Mock Roles, not objects", zu finden über Google.

Ach ja: Räume deine tests regelmässig auf, nutze factory methoden wo es nur geht, vermeide unbedingt Redundanz. Wenn man zB. einen Konstruktor ändert und 4 Testklassen können nicht mehr kompiliert werden, mache deine Änderung rückgängig, und sorge dafür das der Konstruktor in den tests nur noch von einer einzigen Factory Methode direkt verwendet wird.


----------



## ARadauer (13. Jul 2011)

> muss ich ja irgendwo das Verhalten der Mocks definieren


kennst du mockito?


----------



## Antoras (14. Jul 2011)

schalentier hat gesagt.:


> Mocks sind dazu gedacht, Abhaengigkeiten zu komplexen Klassen "wegzumocken". Das aendert sich nicht, wenn sich irgendwelche Implementierungen aendern.





schalentier hat gesagt.:


> Mocks werden nicht "gelöscht" wenn man mit dem test fertig ist, mocks bleben, weil sonst das SUT (Subject under test, also die zu testende klasse/Einheit) nicht mehr isoliert ist.


Und was passiert wenn die Abhängigkeiten irgendwann implementiert werden? Ich muss ja irgendwann gucken ob die einzelnen SUTs auch miteinander arbeiten können und nicht nur mit den Mocks. Dafür bräuchte ich ja dann zusätzliche Tests, wenn die bestehenden unangetastet bleiben.


ARadauer hat gesagt.:


> kennst du mockito?


Ja, ich hab damit gemeint was passieren soll wenn ein Test geschrieben wird, der nicht weiß ob er mit Mocks oder mit konkreten Implementierungen arbeitet.


----------



## schalentier (14. Jul 2011)

Antoras hat gesagt.:


> Und was passiert wenn die Abhängigkeiten irgendwann implementiert werden? Ich muss ja irgendwann gucken ob die einzelnen SUTs auch miteinander arbeiten können und nicht nur mit den Mocks. Dafür bräuchte ich ja dann zusätzliche Tests, wenn die bestehenden unangetastet bleiben.



Exakt, diese zusaetzlichen Tests heissen dann Integrationstests. Die anderen (die die Mocks benutzen) sind normalerweise Unittests, d.h. sie testen nur und ausschliesslich die "Unit" (normalerweise die Klasse).


----------



## maki (14. Jul 2011)

> Und was passiert wenn die Abhängigkeiten irgendwann implementiert werden?


Nix.



> Ich muss ja irgendwann gucken ob die einzelnen SUTs auch miteinander arbeiten können und nicht nur mit den Mocks.


Das sind keine isolierten Unittests mehr.



> Dafür bräuchte ich ja dann zusätzliche Tests, wenn die bestehenden unangetastet bleiben.


Richtig, meist wird das dann gleich in integrationstests/funktionalen tests gemacht.


----------



## Antoras (14. Jul 2011)

Ok, dann wird das ein bisschen klarer. Könnt ihr mir gute Bücher/Artikel empfehlen, die einen Einstieg in das Thema Integrationstests bieten?


----------



## maki (14. Jul 2011)

"XUnit Test Patterns - Refactoring Test Code" von Gerard Meszaros
Darin geht es um alle möglichen Arten von Tests.


----------



## Antoras (14. Jul 2011)

Ok, danke für den Vorschlag. Werde ich mir mal angucken.


----------

