Technik- und Business-zentrierte Tests


Christoph Mathis , Mo, 08.11.2010 - 19:42

Scrum beschäftigt sich in letzter Zeit stärker mit der Frage, wie die Lücke zwischen der Spezifikation und dem mit TDD entwickelten Code überbrückt werden kann. Dazu muss man Zwischenschritte zwischen der Spezifikation und der Implementierung entwickeln über:

  • User Stories
  • Akzeptanzkriterien
  • Definition of Done
  • Akzeptanztests
  • Akzeptanztestgetriebene Entwicklung

User Stories haben eine empfohlene Darstellungsform mit den Teilen: Wer, was warum. Das ist aber nur ein Vorschlag. Die Formulierung von Ron Jeffries kreist stärker um das Wesen:

  • Card: Stories werden traditionell auf Karteikarten geschrieben. Damit kann schon gar nicht die Illusion auskommen, das seine eine vollständige Spezifikation (dafür wäre ein schlicht lächerliches Format).
  • Conversation: vielmehr offenbaren sich die Details hinter einer Story oft erst in einem Gespräch über die Story
  • Confirmation: Akzeptanzkriterien und die daraus entwickelten ersten Akzeptanztests bestätigen, daß die Story korrekt umgesetzt wurde

Akzeptanztests sind also Tests auf der Ebene der Business-Funktionalität. Sie setzen zum Teil direkt Akzeptanzkriterien um. Jetzt kann ich versuchen, die Schritte der akzeptanztestgetriebene Entwicklung (ATDD) zu definieren:

  • Zu User Stories werden Akzeptanzkriterien entwickelt
  • Wo immer ich feststelle, daß ich mich wieder hole und immer die gleichen Kriterien formulieren, kann diese herausziehen: das ist der Kern einer Definition of Done (ggfls. zusammen mit externen Vorgaben)
  • Aus den Kriterien entwickeln wir die ersten Akzeptanztests. Damit haben wir ein wesenntlich mächtigeres Mittel zur Fomrulierung als nur die verbale Spezifikation in der Hand: eine ausführbare Spezifikation. Diese können (von Kunden, dezidierten Testern oder auch von Entwicklern) auch zuerst entwickelt werden, bevor die Implementierung des Business-Codes startet
  • Diese Akzeptanztests leiten dann die Implementierung mittels TDD

Dieser Zyklus kann einige Stunden bis zu einem Sprint gehen. In der Kombination mit einer Entwicklung mit TDD er stellt sicher, daß das Richtige und daß die Implementierung richtig entwickelt wurde und die Entwicklung insgesamt berechenbarer wird. Zuerst eine Abgrenzung. Wir reden von

  • Testautomatisierung
  • Kontinuierliche Integration
  • Test-First
  • Testgetrieben

Testautomatisierung ist noch relativ einfach: Test, die von Anfang bis Ende ohne manuelle Eingriffe laufen und damit wiederholbare Testergebnisse erlauben. Möglichst soll eine ganze Testsuite mit einem Knopfdruck gestartet werden. Der Nutzen dabei liegt in der Option wesentlich tiefgreifendere Änderungen in einem System einzuführen. In einer traditionellen Arbeitsteilung, in der Tester das ganze immer wieder durchklicken müßten, würden diese gar nicht mehr sinnvoll nachkommen. Kontinuierliche Integration ist die sinnvolle Fortsetzung und als Konsequenz relativ einfach: wenn es diese Tests gibt, dann sollte man auch sicherstellen, daß sie auch zeitnah ausgeführt werden. Möglichst immer dann, wenn eine Änderung im Versionsverwaltungssystem eingecheckt wird. Oft wird dann noch die Forderung erhoben, die Tests vor der Implementierung zu schreiben und dann die Implementierung (Test First). Ich bin in diesem Punkt skeptisch, ich habe oft erlebt, daß diese Diskussion sich an der Geschmacksfrage festmacht, d.h. an den persönlichen Vorlieben des einzelnen Entwicklers. Testgetriebene Entwicklung (TDD) ist da konsequenter und paradoxerweise leichter zu vertreten. Es ist ein Ansatz, mit dem sichergestellt wird, daß

  • Jedes Codestück durch einen (Unit-)Test motiviert ist. Der Test wird dadurch tatsächlich zu einer ausführbaren Spezifikation. Man schreibt gerade so viel Code, daß der Test nicht mehr fehlschlägt – und dann wechselt man den Hut und refakturiert – ohne neue Funktionalität zu schreiben – bis man mit dem Programm zufrieden ist.
  • Jedes Codestück ist dadurch auch per Definition testbar
  • Der gesamte Code ist auch so oft mit Refakturierung durchgeknetet worden, daß sich das System wesentlich anpaßbarer verhält. Anpaßbar heißt auch, daß initiale Entscheidungen, z.B. Architektur, stärker revidiert werden können – das ist wichtig in einer Umgebung, in der ich sowieso noch nicht alles vor dem Start der Entwicklung weiß.
  • Dieser TDD-Zyklus wird normalerweise in sehr kurzen Zyklen durchlaufen. Kurz heißt hier wenige Minuten und ist der ständige Rhythmus in der Arbeit eines Entwicklers oder eine Pairs.

Wenn man diese Stufen ansieht, bemerkt man, daß sie inkrementell immer tiefer Strukturen der Software hinterlassen. Man sieht es der Software an, wenn sie testgetrieben entwickelt wurde und man hat deutliche Unterschiede nicht nur in er inneren Qualität, sondern durch die bessere Änderbarkeit auch in den Kosten über den gesamten Lebenszyklus. Jetzt stimmt auch die innere Qualität der Software: jedes Codestück ist durch einen Unit-Test motiviert und es macht das richtige - der äußere Zyklus des ATDD wurde gerade dafür gemacht.