Im Testumfeld haben sich eine Vielzahl von Begriffen etabliert, welche wir in einer losen Folge von Artikeln besprechen werden. Diesmal geht es um die Teststufe Unittest.

Inhalt:

Testst​​​ufen

Teststufen ordnen Tests zeitlich nach dem Entwicklungsstand der Software und haben das Ziel, die jeweilige Entwicklungsstufe durch dem Entwicklungsstand entsprechende Testfälle und -arten abzusichern:

Teststufen im Überblick

Die Teststufen orientieren sich hierbei an den Entwicklungsstufen des V-Modells und sind somit natürlich durch klassische Vorgehensmodelle wie das Wasserfall-Modell geprägt, passen aber auch für agile Vorgehensmodelle – wenn dort natürlich auch in wesentlich höherer Frequenz (siehe bspw. Continuous Testing). Andere Testansätze wie bspw. Netflix’ Chaos-Monkey kommen im agilen Umfeld ergänzend hinzu und werden separat betrachtet.

Unittest

Der Unittest, auch Entwickler-, Komponenten- oder Modultest genannt, ist i.d.R. die erste Teststufe und erfolgt zeitlich während oder auch schon vor (Test-Driven Development; TDD) der Entwicklung durch den Entwickler. Testgegenstand sind die einzelnen Module, Funktionen, Methoden oder Klassen innerhalb eines Programms. APIs können darüber hinaus als Testgegenstand im Rahmen von Unittests mit getestet werden. Da der Entwickler Kenntnis über Aufbau und Funktionsweise seines zu testenden Sourcecode hat, handelt es sich in Abgrenzung zum Black-Box-Test, bei dem man keinen Einblick in den Testgegenstand hat, um einen sog. White-Box-Test.

Der entwicklungs-begleitende Unittest ist seit vielen Jahren etablierter Standard in der Softareentwicklung. Eine hohe Testabdeckung im Unittest gibt insb. dem Entwickler ein »Sicherheitsnetz«, welches vor unbeabsichtigten Seiteneffekten, welche sich mit Code-Anpassungen ergeben, schützt. Dadurch werden bspw. Refactorings erleichtert, da es so möglich ist, Codeumstellungen einfach auszuprobieren; die Auswirkungen lassen sich jederzeit durch die Unittests verifizieren.

Als Metrik hat sich für Unittests die Testabdeckung (Tes​​​​​tcoverage) etabliert, bei der üblicherweise mittels kontrollflußorientierter Verfahren festgestellt wird, wieviel Prozent der Programmzeilen von entsprechenden Testfällen abgedeckt werden:

Testabdeckungsbericht - Übersicht

Hinsichtlich Ermittlung des Kontrollflußes bzw. der Testabdeckung existieren wiederum eine Vielzahl von Ansätzen. Erfahrungsgemäß ist hier weder die genaue Ausprägung der Ermittlung der Testabdeckung noch der absolute Abdeckungsgrad (»98,5 %«) entscheidend, sondern dass die Testabdeckung regelmäßig (bspw. nach jedem einchecken / commit oder Build) gemessen wird und sich der Entwickler für seinen Code Gedanken macht, was die jeweilige Abdeckung bzw. ein entsprechend steigend oder fallender Wert zu bedeuten hat. Im folgenden Beispiel wurde ein Ast einer Verzweigung nicht durch einen entsprechenden Testfall abgedeckt, was im konkrekten Fall zwar die Code Coverage auf 93,75 % drückte, aber als verschmerzbar eingestuft wurde:

Testabdeckungsbericht - Details

Als Faustformel für die Unittestabdeckung kann somit gelten:

Optimale Unittestabdeckung = 100 % - gesunder Menschenverstand

Populärstes Tool im Umfeld der Unittests ist sicherlich JUnit, wobei es mittlerweile für jede Programmiersprache entsprechende Unterstützung, zum Teil auch schon direkt in der jeweiligen IDE gibt. Theoretisch lassen sich Unittests auch ohne Tooling durchführen, da es sich im Kern wiederum nur um ein Programm handelt, welches die zu testenden Klassen einbindet und deren Methoden über eigene Methoden (die Testfälle) entsprechend aufruft und verifiziert.​

Die Unittests werden i.d.R. direkt auf dem Rechner des Entwickler / der Entwicklungsumgebung ausgeführt und nicht in einer besonderen Testumgebung.

Der Unittest »gehört« dem Entwickler; die genaue Ausführung und auch etwaige Metriken obliegen der Verantwortung des jeweiligen Entwicklungsteams, da eine Diskussion der Testabdeckung mit Außenstehenden aufgrund des White-Box-Charakters wenig zielführend ist. Best Practice ist hier eine entsprechende Vereinbarung in den Coding Conventions des und die Diskussion im jeweiligen Entwicklungsteam. Um diese Teststufe in Entwicklungsorganisationen zu etablieren, kann es allerdings helfen, die gemessene Testabdeckung als Liefergegenstand mit dem Code an die Qualitätssicherung / nächste Teststufe zu übergeben. Dadurch bekommt der Unittest verbindlichen Charakter, womit andererseits auch sichergestellt sein muss, dass der Entwickler entsprechend Zeit für die Unittests bekommt.

Unittests sind vollständig automatisierbar und i.d.R. auch durch das entsprechendes Tooling und die Einbindung in den Build- / Deploment-Prozess vollständig automatisiert.

Durch diese Art von Tests entsteht allerdings auch (zusätzlicher) Code und jede Zeile mehr an Code ist hinsichtlich ihres Mehrwerts kritisch zu hinterfragen, da sie mehr Abhängigkeiten bringt und gewartet werden muss. Dies hat mittlerweile auch einige (prominente) kritische Stimmen dem Unittest gegenüber laut werden lassen (siehe bspw. Why most unittessting is a waste oder etwas genereller DHH).

Unserer Einschätzung nach kommt hier der auch schon oben weiter bei den Metriken aufgeführte gesunde Menschenverstand ins Spiel: man kann etwas Gutes natürlich auch immer übertreiben und damit ins Negative kehren. Man kann außerdem keine Qualität in Software »rein« testen oder um es mit James O. Copliens Worten zu sagen: »Be humble about what tests can achieve. Tests don’t improve quality: developers do.«