Storytelling with tests

Jeder erfahrene Entwickler hat schon mal einmal ein Projekt erlebt (oder „durchgemacht“?) bei dem es gar keine Tests gibt. Schlimmer noch wäre ein Projekt in dem Tests als zusätzlicher Aufwand interpretiert werden. Was ist jedoch mit den Projekten die viele Tests (Unit-, Integrations- oder Smoketests sind hier eingeschlossen) haben. Die laut Sonar sogar eine „akzeptable“ Coverage (dieser Wert muss natürlich von jedem Team oder Projektleiter selbst definiert werden) hat? Sind diese Projekte besser oder schlechter aufgestellt? Natürlich ist das eine rhetorische Frage. Diese Projekte sind jedoch nur bedingt besser aufgestellt. Im Lebenszyklus eines Projektes ist es völlig normal, dass Entwickler ausgetauscht werden. Und die neuen Entwickler refactoring betreiben. Für diesen Fall sind die Tests sicherlich gut um bestehende Funktionalität zu gewährleisten. Was jedoch wenn diese Entwickler neue Funktionalität implementieren und bestehende auch anfassen müssen. So kann es durchaus passieren, dass gewisse Fälle gar nicht mehr auftreten können für getestete Methoden.

Was tut der Entwickler mit diesen Testfällen in den Tests (ab diesem Abschnitt geht es primär um Unittests)? „Löschen natürlich“ würden die meisten sagen. Wie erkennt man aber diese mittlerweile obsoleten Tests, wenn einige Testmethoden „testSaveProduct1“, „testSaveProdukt4711“ oder „deleteProdukt“ heißen. Weiß jeder aus dem Stegreif was diese Methoden testen? Welcher Vorgang getestet wird, kann man herauslesen. Aber wie schaut es mit dem Ausgang aus? Weiß man bei diesen Testnamen, ob der Ausgang positiv oder negativ ist? Sollte „testSaveProduct1“ auf den Erfolg prüfen? Oder soll es eine Exception werfen, weil nicht alle fachliche Bedingungen erfüllt sind, um das Produkt zu speichern? „Ja das kann man ja aus dem Quellcode heraus sehen“, kriegt man oft zu hören. Aber warum soll ich erst in den Quellcode gucken, um zu sehen welcher Ausgang erwartet wird, wenn der Test im lokalen Build oder im CI Server fehlschlägt? Das ist immer zusätzlicher Aufwand den man sich eigentlich immer sparen möchte.

Aber was sind eigentlich Anforderungen für Tests bzw. Testnamen? Für mich gibt es folgende Anforderungen die zwingend erfüllt werden müssen:

  1. dokumentierende und testende Funktionalität
  2. beschreibender Name
  3. erwarteter Ausgang der Methode
  4. einfache und schnelle Verständlichkeit
  5. aufzurufende Methode sollte schnell erkennbar sein

Sollten nicht alle Anforderungen erfüllt werden, kann die Entwicklung schnell in das o.g. Szenario fallen.

Also ist die Wahl des Namens der Methode essentiell. Ähnlich wie bei der Wahl des Methodennamens der die Funktionalität ausführt ist die Wahl des Namens für die Testmethode schwer. Hier sollte sich auch immer der Entwickler lieber 1 Minute mehr Zeit nehmen als zu wenig. Die Kollegen und Folgeentwickler werden dankbar sein.

Welche Probleme gibt es hier bei der Namensfindung?

  • komplexes Domänensetup für diesen Testfall
  • Bedingungen für diesen Testfall in klare, kurze Worte zu fassen
  • Viele verschiedene Varianten von Ausgängen einer Testmethode
  • Findung der richtigen Abstraktion einer Methode

Beispiel einer zu testenden Methode:

public User registerUser(User user) {
   validateUser(user);
   generateId(user);
   addUserRole(user);
   registerInteral(user);
   activateUser(user);
   return user;
}

Ich denke eine kurze Erläuterung zu dieser Methode ist nicht nötig, da anhand der Methodennamen schon deutlich ist, welche Aufgabe sie erfüllen sollen. Info vorab: Namensgebung und Namensdomänen sind immer eine Einstellung der Philosophie. Jeder mag es doch ein wenig anders.

Wie könnten jedoch nun die Testmethoden für dieses Beispiel aussehen:

public void registerUserWithValidInputSuccessFulRegistersUser()
public void registerUserWithValidInputCreatesDefaultRole()
public void registerUserWithValidInputSetsIdentifier()
public void registerUserWithValidInputActivatesUser()

Folgende Notation ist auch gerne gesehen und angenommen:

public void registerUser_WithValidInput_SuccessFulRegistersUser()
public void registerUser_WithValidInput_CreatesDefaultRole()
public void registerUser_WithValidInput_SetsIdentifier()
public void registerUser_WithValidInput_ActivatesUser()

Wie sieht nun die Notation für die Namen aus? Diese besteht aus 3 Teilen:

  1. Methode die getestet werden soll
  2. Bedingungen die für diese Methode gelten
  3. erwarteter Ausgang bzw. Effekt der Methode

Es ist möglich diese Teile durch Unterstriche voneinander zu trennen oder aber auch einfach die Teile aneinander reihen um sie mit einander zu verbinden. Es ist absurderweise möglich, die verschiedenen Bereiche durch ein $ zu trennen (Link zur Java Specification), doch das führt natürlich nicht zu einer besseren Lesbarkeit. Wie oben schon genannt ist dies eine Einstellung der Philosophie. Jeder Entwickler hat seine eigenen Präferenzen.

Sollte es nun zu der Situation kommen, dass min. 1 von den 4 oben genannten Problemen auftritt, so ist dies ein gutes Anzeichen dafür das die zu testende Methode eine zu „große“ Aufgabe hat und diese entsprechend verkleinert oder vereinfacht werden sollte.

Teilen Sie diesen Beitrag

Das könnte dich auch interessieren …

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert