Viele Softwareprojekte starten mittlerweile mit dem Anspruch testgetrieben entwickelt zu werden. Der Anfang gestaltet sich in der Regel einfach und die Entwickler erkennen schnell die Vorzüge von TDD. Leider kommt es in vielen Fällen im Laufe der Zeit dazu, dass die Wartung der Tests mehr und mehr Zeit in Anspruch nimmt. Die Entwickler sind frustriert und TDD wird vom Management als „zu wartungsintensiv“ abgewiesen.
Ziel dieses Vortrags soll es sein, dort einzusteigen wo Tutorials und Einsteigerseminare und -bücher aufhören. Wir wollen dem erfahrenen Entwickler Tools und Vorgehensweisen an die Hand zu geben, um diesem „Wartungsalbtraum“ zu entgehen. Das echte Leben ist komplexer als das Taschenrechnerbeispiel!
2. Agenda
• Einführung
– (sehr) kurze Einführung in TDD
– Test Doubles
– Vorstellung der Tools
• Keeping it clean
– Best Practices
– 10 Dinge um dein Design zu zerstören
– Auf die Tests hören
Real Life TDD, 15. Januar 2014 2
5. Test-Driven-Development a
Real Life TDD, 15. Januar 2014 5
• Warum?
– Korrektheit
– Sauberes Design
– Ermöglicht Refactorings
– Seelenfrieden
• Wann?
– Test first!
– TDD ist Teil des Entwicklungsprozesses und sollte
immer mit eingeplant werden
7. Test-Driven-Development a
Real Life TDD, 15. Januar 2014 7
1. Test schlägt fehl
– Vor der Funktionalität kommt der Test
• assertEquals(„foobar“, cut.toString())
2. Test erfolgreich
– Die Annahmen im Test werden bedient
• return „foobar“
3. Refactoring
– Vereinfachen, Konsolidieren, Verschieben etc.
8. Test Doubles
• „generic term for any kind of pretend object
used in place of a real object for testing
purposes“ – Martin Fowler [1]
– Dummy
– Fake
– Stubs
– Mocks
• Was wollen wir testen?
– State oder Behaviour
Real Life TDD, 15. Januar 2014 8
11. JUnit
• Einführung von Rules seit JUnit 4.7 [2]
• Vordefinierte Regeln
– TemporaryFolder
– Timeout
– ExpectedException
– ErrorCollector
– TestName
Real Life TDD, 15. Januar 2014 11
12. JUnit
• Eigene JUnit Regeln erstellen
– Interface TestRule implementieren
– Vorhandene Templateklassen verwenden
• ExternalResource
• TestWatcher
• Verifier
Real Life TDD, 15. Januar 2014 12
13. JUnit
• Parametrisierte Tests
– Kompakte, lesbare Testmethoden
– DRY Prinzip in den Tests
• Aber
– Umständlich in plain JUnit
– JUnitParams („ Parameterised tests that don't
suck“)
– Oder gleich Spock ;-)
Real Life TDD, 15. Januar 2014 13
14. • Mocking framework
• Warum Mockito?
– Einfaches Erzeugen von Mock Objekten
– Annotationen erleichtern das Setup
– Einfache API, compile-safe
– Unterstützt viele Anwendungsfälle
• Mocks, Stubs, Partial-Mocks, Spies
– Hervorragende Dokumentation
Real Life TDD, 15. Januar 2014 14
Mockito
15. Hamcrest
• Matcher Bibliothek [3]
• Komplexe assertXY Ausdrücke werden häufig
unleserlich
• Seit JUnit 4.4:
– assertThat(T actual,
org.hamcrest.Matcher<T> matcher)
Real Life TDD, 15. Januar 2014 15
16. AssertJ
• Fluent Matcher Bibliothek [4]
assertThat(fellowshipOfTheRing)
.hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
Real Life TDD, 15. Januar 2014 16
21. Best Practices
• Eine Testklasse pro Class-under-Test
• Testname soll ausdrücken was getestet wird
• Ein assert/verify pro Test
• Unabhängige Tests
• Blitzschnell!
• Tipps:
– Import von statischen Methoden
– Code Coverage (z.B. CodePro)
Real Life TDD, 15. Januar 2014 21
22. 10 Dinge um dein Design
zu zerstören
1. Services da instanziieren wo sie gebraucht werden
2. Law of Demeter Verletzung
3. Logik im Constructor
4. Global State
5. Singletons
6. Statische Methoden
7. Implementierung vererben
8. Polymorphismus nachprogrammieren
9. Services und Values vermischen
10. Mehr als eine Sache tun (Separation of Concerns) [5]
Real Life TDD, 15. Januar 2014 22
23. Auf die Tests hören
1. Schwer zu mockende Objekte
2. Mocken von konkreten Klassen
3. „Wie mocke ich eine private Methode?“
4. Mocken von Value Objekten
5. Aufgeblähter Constructor
6. Zu viele Erwartungen
Real Life TDD, 15. Januar 2014 23
Korrektheit = Fehlerfrei
Sauberes Design durch strikte Trennung von SUT und Collaborator
Seelenfrieden durch hohe Testabdeckung, erleichtert Refactorings
Test first! Sonst gehen grundlegende Vorteile verloren
TDD ist grundlegendes Tool und muss mit eingeplant werden
Dummy
Objekte die durchgereicht, aber niemals verwendet werden
Fake
Funktionierende Implementierung die aber nicht für den produktiven Einsatz bestimmt ist (z.B. in-memory DBs)
Stub
Liefert vorher definierte Antworten (z.B. FTP, E-Mail) und zeichnet ggfs Verwendung auf
Mock
Objekte die Erwartungen definieren und überprüft werden können (behaviour!)
Test state or behaviour?!
TemporaryFolder
Verwalten von temporären Verzeichnissen/Dateien
Timeout
Globalen Timeout definieren
ExpectedException
Erwartete Exception, bietet zusätzliche Funktionen wie das Überprüfen der Message
ErrorCollector
Führt alle Asserts aus und sammelt Fehler
TestName
Gibt den Namen des aktuellen Tests wider
ExternalResource
Stellt Ressourcen in einer before() Methode bereit, räumt in after() wieder auf
TestWatcher
Benachrichtigung über Testausführung (reagieren auf fail, success)
Verify
Überprüfungen nach dem Test
Code zeigen!
ExternalResource
Stellt Ressourcen in einer before() Methode bereit, räumt in after() wieder auf
TestWatcher
Benachrichtigung über Testausführung (reagieren auf fail, success)
Verify
Überprüfungen nach dem Test
Code zeigen!
Einfaches Erzeugen von Mock Objekten
Es müssen keine expectations formuliert werden
Annotationen
Mock, InjectMocks, Captor
Einfache API
Statische Methoden lassen sich importieren -> sauberer testcode
Echte API in Mocks -> compile-safe
Mocking and verification
Stubbing
Argument matching
Verifying exact number of invocations
Stubbing void methods
Vorheriger Titel: 10 Dinge um den Code untestbar zu machen
Powermock schafft Abhilfe
Dependencies und Collaborator werden versteckt, Abhängigkeit zu Implementierung
Mocken von ganzen Objektgraphen
Wissen implizit, schwer nachvollziehbar
Verletzt die Unabhängigkeit von Tests
Gleiche wie global State
Ähnlich wie 1
Wir müssen Dinge mocken die wir gar nicht verwenden/testen wollen
Häufig einfacher und lesbarer Polymorphismus zu verwenden
Aufbau von solchen Hybriden meist problematisch, Values sollten immutable sein
Sollte klar sein
Warum ist das Objekt schwer zu mocken
„Clients should not be forced to depend upon interfaces that they do not use“
Separate Klasse?
Was will man mocken? Zu kompliziert? Builder!
Zu viele Abhängigkeiten -> Konsolidieren!
Schwer zu unterscheiden was setup ist und was getestet wird