22. November 2024 von Steffen Albrecht
„Test First“ beginnt für Entwicklende im Refinement
Warum Tests schreiben, wenn der Code schon fertig ist? Frühes Testen beginnt nicht im Code, sondern im Refinement. In diesem Blog-Beitrag zeige ich euch, wie „Example Mapping“ hilft, Test-First direkt in das Refinement zu integrieren. Dies stellt sicher, dass die Anforderungen von Anfang an klar sind und ermöglicht schnelleres und besseres Feedback.
Warum Testen?
Zunächst: Warum testen wir? Die einfachste Antwort, „um zu testen, ob das, was wir gebaut haben, auch funktioniert“, ist zwar nicht falsch, führt aber zu einem Konflikt und einem großen Problem.
Wenn wir nur das testen, was wir gebaut haben (ich weise hier auf die Vergangenheitsform hin!), dann ist es viel praktischer, die Tests erst im Nachhinein zu erstellen. Warum also „Test first“? Das Testen im Nachhinein führt aber dazu, dass nur das getestet wird, was wir gebaut haben, und nur das. Aber haben wir es richtig gebaut? Und wenn ja, haben wir auch das Richtige gebaut? Das können uns solche nachträglichen Tests nicht sagen. Wir brauchen also eine bessere Antwort auf die Frage "Warum testen wir?
Werfen wir einen Blick auf die Vorgehensweisen im Software-Engineering. David Farley hat sie in seinem Buch „Modern Software Engineering“ sehr gut beschrieben.
- Iteratives Arbeiten
- Inkrementelles Arbeiten
- Experimentelles Arbeiten
- Empirisches Arbeiten
- Erhalten von schnellem, qualitativ hochwertigem Feedbac
Automatisierte Tests unterstützen alle diese Punkte, wenn sie nicht sogar unverzichtbar sind. Insbesondere der letzte Punkt, „Erhalten von schnellem, qualitativ hochwertigem Feedback“, ist hier hervorzuheben. Das schnellste Feedback, das die meisten Entwicklerinnen und Entwickler kennen, ist der Inline-Compiler moderner IDEs. Ein falsches Zeichen und wir wissen sofort, dass das, was wir gerade erstellt haben, nicht funktioniert und bekommen in der Regel sofort einen Hinweis, wie wir das Problem beheben können. Das Feedback von automatisierten Tests kommt erst später, wenn man die Tests nachträglich erstellt. Folgt man dagegen konsequent dem Test-First-Ansatz, erhält man das Feedback viel früher, auch ohne den Test schon ausgeführt zu haben, und damit viel schneller als selbst der beste IDE-Compiler! Das „Geheimnis“ liegt im Testzyklus.
Es beginnt mit „Write a failing test“ und hier liegt bereits der Schlüssel zu einem frühen Feedback. Um einen Test zu schreiben, der nicht funktioniert, brauchen wir Antworten auf Fragen wie „Was wollen wir testen?“ und „Welche Bedingungen müssen erfüllt sein, damit der Test funktioniert?“. Wenn wir darüber mit den richtigen Personen sprechen, erhalten wir bereits erste wertvolle Hinweise.
Test-First im Refinement
Als Entwicklerin oder Entwickler werden wir in der Regel zuerst im Refinement mit den neuesten Umsetzungswünschen konfrontiert. Dabei sollten zumindest der Umfang und die Kriterien, die eine erfolgreiche Umsetzung definieren (auch Akzeptanzkriterien (AK) genannt), geklärt werden. Das besprechen wir mit unseren Kolleginnen und Kollegen sowie den beteiligten Stakeholdern, denn hier sitzen genau die richtigen Leute. Warum dann nicht gleich mit Test-First beginnen und erstes Feedback einholen? Das Gute ist, dass die meisten Entwicklerteams das auf einer informellen Ebene schon machen. Nur informell darüber zu plaudern ist allerdings nicht sehr professionell. Refinement-Meetings, die sich zu elend langen und quälenden „Spezifikations-Workshops“ entwickeln, führen oft auch nicht zu besseren Ergebnissen und sind meist nur schrecklich langweilige Zeitvernichter.
Einen Ausweg bietet hier die Methode „Example Mapping“. Das Refinement beginnt wie gewohnt mit der Vorstellung der zu lösenden Aufgabe, hier der Einfachheit halber „Story“ genannt. Fragen werden gestellt und diskutiert. Fragen, die nicht beantwortet werden können, werden explizit notiert. Die Story ist also noch nicht „fertig“. Sobald die Verständnisfragen geklärt sind, müssen die AKs der Story geklärt werden. Jede Story sollte mindestens ein Akzeptanzkriterium (AK) haben. Jede Umsetzung ist mit Aufwand verbunden. Warum sollte man in etwas investieren, dessen Umfang völlig unklar ist? Deshalb gilt auch hier: Hat eine Story kein AK, ist sie nicht bereit. Die AKs werden, wie üblich, auf der Story vermerkt. Doch welche Qualität haben die AKs? So etwas wie „Alle weltweit verfügbaren Zahlungsmethoden sind möglich“ könnte etwas langatmig werden. Leider sind nicht alle AKs so klar als „ungünstig“ zu erkennen. Hier kommt das eigentliche „Example Mapping“ ins Spiel. Für jeden AK sollte mindestens ein Beispiel definiert werden, mit dem die Umsetzung demonstriert werden kann. Kann für einen AK kein Beispiel gefunden werden, so ist dieser AK zu streichen oder in eine offene Frage umzuwandeln.
Das erste Example sollte den „Happy Path“ darstellen. Gibt es mehrere, wird man schnell zu weiteren Examples kommen. Erfahrene Produktentwickler denken hier auch an Grenz- und Fehlersituationen. Allen Examples ist gemeinsam, dass sie den Implementierungsaufwand nicht erhöhen. Tun sie es doch, ist das ein Zeichen dafür, dass es sich nicht um ein Example, sondern um einen AK handelt.
Moment, hier könnte man denken, dass Fehlerbehandlungen dann keine Examples sind, da sie den Implementierungsaufwand erhöhen. Für Prototypen mag das zutreffen. Funktionen für den produktiven Einsatz werden ohne Fehlerbehandlung selten Freude bereiten.
In der Regel kann jede Story anhand ihrer AKs geschnitten werden und eine Story, die nur Fehlerbehandlung enthält, ist keine Story, sondern ein Bugfix!
Für die Beschreibung der Examples hat sich das Format „Given - When - Then“ bewährt, da es auch von „Nicht-Technikern“ gut verstanden wird. Betrachten wir das eingangs erwähnte AK „Alle weltweit verfügbaren Zahlungsmethoden sind möglich“. Da „alle“ nicht realisierbar ist, könnte man das AK ändern in „Alle gängigen verfügbaren Zahlungsmethoden sind möglich“. Beginnt man hier mit Examples wie zum Beispiel „Gegeben sind VISA-Zahlungsinformationen (Kartennummer, Ablaufdatum, Kartenprüfnummer), wird der Kauf bestätigt, wird die Zahlung durchgeführt und eine Bestätigung der erfolgreichen Zahlung angezeigt“, wird schnell klar, dass jede weitere Zahlungsmöglichkeit zu Mehraufwand führt. Wenn man anfängt, die Fehlerbehandlung zu beschreiben, dann wird das ein sehr langes Refinement. Daher würde man hier schnell dazu kommen, den AK zu ändern in: „Zahlung mit VISA ist möglich“. Die relevanten Examples sind nun überschaubar. Alle anderen Bezahlmöglichkeiten mit ihren spezifischen Randbedingungen werden zu eigenen AKs oder eigenen Stories. Die klare und einfache Beschreibung der Examples ermöglicht es, mit den Stakeholdern und unseren Produktmenschen zu einem effektiven Refinement zu kommen, mit großer Klarheit, was umgesetzt werden soll.
Fazit
Test-First bedeutet, von Anfang an in Tests zu denken. Wenn wir eine gemeinsame Sprache mit unseren Stakeholdern finden, zum Beispiel durch Example Mapping, kommen wir durch schnelles Feedback zu besseren Anforderungen. Anforderungen, die von allen Seiten verstanden werden, deren Umsetzung klar und strukturiert erfolgt und bei denen von Anfang an definiert ist, wann die Umsetzung erfolgreich abgeschlossen ist. Ohne Code ist kein Compiler schneller.
Ihr möchtet gern mehr über spannende Themen aus der adesso-Welt erfahren? Dann werft auch einen Blick in unsere bisher erschienenen Blog-Beiträge.
Auch interessant: