<link rel="stylesheet" id="astra-theme-css-css" href="https://pmo-it.de/wp-content/themes/astra/assets/css/minified/style.min.css?ver=3.6.9" media="all">

Fünf Tipps für besseren Testcode

5 Tipps für besseren Testcode Blogpost

Good Practices

In diesem Blogpost zeige ich fünf Good Practices auf, um den eigenen Testcode zu verbessern.
Aber wieso nur “Good Practices”?

Code und Codestyles ändern sich ständig, was heute als richtig anerkannt wird könnte morgen schon wieder falsch sein. Aus diesem Grund möchte ich hier nicht von “Best Practices” sprechen. Möglicherweise können in der Zukunft neue Erkenntnisse aufkommen, die eine andere Schlussfolgerung zulassen.

Die nachfolgenden Tipps sind in keiner wertenden Reihenfolge angeordnet.

1. Vermeide IF/ELSE im Testcode

Testcode sollte immer gleich ausgeführt werden, das heisst gleiche Anfangsbedingungen und gleiche Endbedigungen. If/Else Blöcke erhöhen die Komplexität und erschweren unnötig das Nachvollziehen der ausgeführten Schritte.

Ist If/Else nötig, weil beispielsweise auf Änderungen am Ausgangszustand der Testumgebung (System under Test oder kurz: SUT) reagiert werden muss, empfiehlt es sich (falls möglich) die Testdaten bzw. Testumgebung anzupassen. Gleiches gillt auch für Switch-Anweisungen. Diese sollten ebenfalls so gut es geht vermieden werden.

2. Clean Code

Wie auch bei der Programmierung vom eigentlichen System, sollten die gleichen Clean Code Prinzipien auch beim Testcode angewendet werden. Das Thema Readability und Maintainability stehen hier im Vordergrund. Erreicht wird dies beispielsweise durch Code auslagern in Unterfunktionen. Grundsätzlich sollten die gleichen Qualitätsanforderungen wie beim Produktionscode eingehalten werden.

3. Vermeidung unnötiger Kommentare

Kommentare sind ein Streitthema für sich. Im Testcode sollten sie allerdings sparsam eingesetzt werden. Kommentare veralten und müssen immer gepflegt werden, dies wird des öfteren bei Änderungen vom Code übersehen. Statt Blöcke zu kommentieren empfiehlt es sich diese Codeblöcke in Unterfunktionen mit sprechendem Namen auszulagern. Somit wird der Kommentar überflüssig und veraltet nicht.

Beispiel:

//...
//ReadCSVFile
BufferedReader br = null;
int ignoreHeader = 0;
String csvSplitSymbol = ",";
String line = "";
    br = new BufferedReader(reader);
    while ( ( line = br.readLine() ) != null) {
        if (ignoreHeader > 0) {
           String[] currentLine = line.split(csvSplitSymbol);
           filecontent.add(currentLine)
        }
        ignoreHeader ++ ;
    }

//Some more Teststeps
//...

Dieser gesamte Kommentar kann eingespart werden, indem der zugehörige Codeblock in eine Unterfunktion ausgelagert wird (siehe bereits erwähnt Clean Code weiter oben).

//...
ArrayList<String> fileContent= ReadCSVFile(filename);
//Some more Teststeps
//...

Natürlich haben Kommentare ihre Daseinsbereichtigung um zum Beispiel Metainformationen über die TestKlassen zu liefern, dennoch sollten sie sparsam eingesetzt werden, um unnötige Wartung zu vermeiden.

4. Das zusammen, was zusammen gehört

Frei nach dem Motto “Das zusammen, was zusammen gehört” ist es sinnvoll, Tests die sich auf die gleiche Funktion oder das gleiche Modul beziehen auch zu gruppieren. Dies bezieht sich nicht nur auf die gleiche Klasse, sondern auch auf das ausführen durch Testtools wie zum Beispiel TestNG. In TestNG lassen sich gleichartige Tests über eine settings.xml Datei zusammenfassen. Dadurch können Suits erstellt werden. Dieses Vorgehen macht es auch einfacher Gruppen für bestimmte Testläufe auszuschließen. Oder aber Test-Gruppen nur bei bestimmten Testläufen (beispielsweise beim Einspielen einer Fehlerbehebung) auszuführen.

Beispiel einer TestNG settings.xml

<suite name="Suite">
  <parameter name="suite-param" value="suite level parameter" />
  <test thread-count="5" name="TestNG">
    <groups>	
		<run>	
   			<exclude name="warenkorb_voll"/>
        </run>	
    </groups>	
    <classes>
      <class name="de.pmoit.schulung.exercise1"/>
    </classes>
  </test>
</suite>

5. Asserts nur am Ende eines Tests und nicht zuviele pro Test

Im Allgemeinen empfiehlt es sich, Tests nach dem Trinity Prinzip oder auch AAA (Arange, Act, Assert) aufzubauen: Zuerst werden Parameter initialisiert und Testvorraussetzungen erfüllt, dann wird der Testvorgang durchgeführt und schließlich das Ergebnis verglichen.
Für gewöhnlich stehen die soll-ist-vergleiche oder auch Asserts genannt, hier am Ende eines Tests. Dies ist auch sinnvoll, da alles was nach einem negativen Assert steht, nicht mehr ausgeführt wird. Mit anderen Worten, schlägt ein Assert fehl, wird alles dannach ignoriert.
Daher ist es auch naheliegend, alle Asserts ans Ende zu stellen und auch nicht zu viele pro Test zu verwenden.
 
Beispiel:
String grass="red";
String sky ="blue";
        
assertEquals(grass, "green");
assertEquals(sky, "blue"); 

Bei diesem einfachen Beispiel schlägt das Assert schon in der Zeile 4 fehl, dies bedeutet Zeile 5 wird nicht mehr ausgeführt. Somit erfahren wir bei einem fehlgeschlagenen Test nicht, ob ein Fehler auch weiter unten im Code besteht oder ob dannach alles in Ordnung ist.
Somit könnten mehr Fehler unerkannt bleiben und erst nach der Behebung des ursprünglichen Fehlers zum Vorschein kommen.

Alternativ können auch weitere Assert-Bibliotheken eingesetzt werden um diese Restriktion aufzuheben. Beispielweise mit AssertJ lassen sich “Softasserts” verwenden. Somit wird es möglich, mehrere Asserts anzuwenden bei denen zum ende des Tests alle Vergleiche ausgewertet werden.

Weiterhin besteht die Wichtigkeit, nur eine zusammenhängende Sache pro Test zu vergleichen und nicht in irgendeiner Form zu mischen. Auch mit Softasserts können ungewollte Nebeneffekte auftreten.

Fazit

Diese 5 Tipps können den eigenen Testcode verbessern, es gibt mit Sicherheit noch viel mehr Dinge auf die ein besonderes Augenmerk zu legen ist, allerdings erscheinen mir diese Grundlagen als wichtig. Ein weiterer wichtiger Faktor ist ebenfalls die Absprache mit den Entwicklern. Es ist immer von Vorteil hier einen gemeinsamen Codingstiel zu finden und sich an gemeinsame Vorgaben zu halten. Nicht nur lassen sich damit Missverständnisse vermeiden und Wartbarkeit erhöhen, es trägt auch zu einem Gemeinschaftsgefühl bei, bei dem ein stetiger Austausch positiv wahrgenommen werden kann.
 

Mehr zum Thema Konfiguration von Gruppen mit TestNG behandle ich in meiner Cucumber Schulung.