In unserer monatlichen Blog-Reihe zu unserem Anti-Pattern-Kalender 2014 zum Thema Testing hier der Kandidat für den Monat März: Larifari.
Ich glaube, das habe ich in “Test_3_neu” getestet. Oder?
Warum spezifisch sein bei der Bennenung von Testfällen? Verständlich ist doch auch: Bestimmter Artikel Substantiv Verb Substantiv Punkt. Alles klar?
Das Problem
Wofür schreiben wir automatisierte Softwaretests? Zum einen, um sicherzustellen, dass die getestete Software funktioniert. Zum anderen aber auch, um zu dokumentieren, was die Software leisten soll.
Was bedeutet es dann, wenn ein Test fehlschlägt? Mindestens ein Teil der Software funktioniert nicht mehr, wie er soll, so dass eine bestimmte Funktionalität nicht mehr gewährleistet ist.
Zum Glück haben wir einen Test geschrieben, der uns nun signalisiert, mit welcher Funktionalität gebrochen wurde. Doch – wer kennt es nicht – leider liegt ein typischer Vertreter des Larifari vor: der Test trägt den vielsagenden Namen „test123“.
Uns bleibt also nichts anderes übrig, als den Test genauer zu analysieren, um zu verstehen was gerade schiefläuft. Das kostet Zeit und Nerven. Wenn zudem alle weiteren Tests ähnlich schlecht benannt worden sind, können wir nicht einmal beurteilen, wie groß der Schaden ist – wir wissen nicht, was noch funktioniert, wenn die anderen Tests schlecht beschrieben worden sind.
Daher ist es wichtig, bei der Benennung der Testfälle gewissenhaft vorzugehen und das erwartete Verhalten möglichst präzise im Testnamen zu beschreiben.
Ein Beispiel
Wenn in folgendem Beispiel der Testfall testInterestRate1()
fehlschlägt, können wir lediglich vermuten, dass etwas mit der Zinsberechnung nicht mehr so funktioniert wie es soll.
public class AccountTest { ... @Test public void testInterestRate1() { account.setBalance(0); assertEquals(0, account.getInterest()); } @Test public void testInterestRate2() { account.setBalance(100); assertEquals(18, account.getInterest()); } }
Die Indizien
- Testnamen geben keinen Hinweis auf das getestete Verhalten
- Fehlschlagende Test lassen keine Rückschlüsse auf die gebrochene Funktionalität zu
- Testfälle enthalten Worte wie „test“ oder werden durchgezählt
Die Lösung
Zum Glück gibt es ein paar einfache Regeln, die man beachten sollte, um Testfälle mit aussagekräftigen Namen zu schreiben.
Kontext nennen – Given
Zuerst gilt es, den Zusammenhang des Testfalls zu beschreiben. In der Regel ist das der Name der getesteten Methode. Es kann aber auch ein fachlicher Kontext sein, der sich über mehrere zu testende Methoden innerhalb eines Moduls bzw. einer Klasse erstreckt. Allerdings kann dann die Fehlersuche wieder ein wenig aufwändiger werden, wenn nicht direkt ersichtlich ist, welche Methode im Test getestet wird.
In unserem Beispiel geht es um die Zinsberechnung eines Kontos Account
. Der Kontext Konto wird in im Klassennamen des Tests ausgedrückt. Auch hier hat es sich bewährt, eine Testklasse pro zu testender Klasse zu erstellen. Diese bekommt dann den Zusatz „Test“ im Namen, um kenntlich zu machen, dass es sich um das Test-Pendant zur Implementierung handelt.
Die Zinsberechnung erfolgt in der Methode getInterest()
. Demnach beginnen unsere Testfälle nun mit „interest“. Prä- oder Postfixe wie „test“ oder „should“ sind überflüssig, da wir wissen, dass es sich um Testfälle handelt.
Hierbei sind natürlich die Besonderheiten bzgl. der Namenskonventionen des verwendeten Testframeworks zu beachten.
Zustand beschreiben – When
Anschließend beschreibt man den Zustand des SUT bzw. des OUT. Wir beschreiben also, welchen fachlichen Fall der zu testenden Methode man sicherstellen möchte.
In unserem Beispiel wollen wir festhalten, wie hoch die Zinsen sein sollen, wenn das das Konto leer ist oder das Konto einen positiven Saldo aufweist. Deshalb nennen wir unsere Testfälle fürs erste interestWhenBalanceIsZero()
und interestWhenPositiveBalance()
.
Schon besser, oder? Jetzt können wir schon recht gut ableiten, welcher Aspekt der Software nicht mehr funktioniert, wenn ein Test fehlschlägt.
Erwartetes Verhalten beschreiben – Then
Doch wir können immer noch nicht mit Bestimmtheit sagen, welches das erwartete Verhalten der Software bzw. des Moduls ist.
Deshalb fügen wir dieses dem Testnamen hinzu.
In unserem Beispiel erwarten wir, dass wir für ein leeres Konto keine Zinsen bekommen und benennen den Testfall demnach interestWhenBalanceIsZeroIsZero
. Wenn Geld auf dem Konto liegt, dann rechnen wir mit 18% Zinsen: interestWhenBalanceIsZeroIs18Percent
.
Um das ganze ein wenig flüssiger und lesbarer zu gestalten drehen wir die Beschreibung um und bekommen folgende Namen für unsere Testfälle: interestIsZeroWhenBalanceIsZero
und interestIs18PercentWhenBalancePositive
.
public class AccountTest { ... @Test public void interestIsZeroWhenBalanceIsZero() { account.setBalance(0); assertEquals(0, account.getInterest()); } @Test public void interestIs18PercentWhenBalancePositive() { account.setBalance(100); assertEquals(18, account.getInterest()); } }
Jetzt haben wir zwei Dinge erreicht:
1. Wir haben eine klare, lesbare Dokumentation darüber, was unsere Software kann.
2. Wenn ein Testfall fehlschlägt, können wir umgekehrt auf einen Blick sehen, was nicht mehr funktioniert.
Zusammenfassung
Wir haben gesehen, dass hilfreiche und informative Testnamen sehr wertvoll und eigentlich unabdingbar sind. Gleichzeitig ist es überhaupt nicht schwer oder aufwändig, klare Beschreibungen für Tests anzulegen.
Mehr noch können uns klare Bezeichnungen beim Schreiben von Tests helfen, da wir im Testnamen zu Beginn genau festlegen, was wir prüfen wollen.
In diesem Sinne: gutes Testen!
Siehe dazu auch James Carrs fantastischen Blogbeitrag zu TDD-Anti-Patterns und die dazugehörige Diskussion auf der Test-Driven Development Yahoo Group.