Das Testframework JaCoCo einbinden: Metriken der Codeabdeckung

https://bildung.social/@oerinformatik/

https://oer-informatik.de/sbb07_jacoco_codeabdeckung

tl/dr; (ca. 11 min Lesezeit): Das Maven-Projekt wird so konfiguriert, dass nach jedem Unit-Test-Durchlauf die Überdeckungstestmetriken gespeichert und als Report aufbereitet werden. Dieser Artikel ist ein Teil der Artikelserie zu einem Adressbuch-SpringBoot-Projekt. Weiter geht es zu gegebenem Zeitpunkt. (Zuletzt geändert am 04.09.2023)

Wir wollen die Möglichkeiten des Testframeworks jUnit erweitern um Metriken, die die Güte der Testabdeckung messen. Welche Programmzeilen werden von den bestehenden jUnit-Tests überhaupt erreicht? Werden alle Bedingungen überprüft? Diese und andere Frage können wir mit Codeabdeckungsmetriken (code coverage) beantworten.

JaCoCo heißt das Framework, dass wir für Java-Projekte nutzen können (das steht für JavaCodeCoverage).1

Wir nutzen die Vorteile der modularen Softwareentwicklung und des Build-Tools Maven, um diese Metriken in unsere bestehende Tests und den normalen Arbeisablauf einbinden zu können. Hierzu ist zunächst einiges an Konfigurationsarbeit nötig. Die meisten Ideen hier sind dem Tutorial von Petri Kainulainen entnommen 2.

Anpassungen in der pom.xml

Um die Testabdeckung automatisch für Unit-Tests zu messen müssen wir:

  • bereits eingebundene Maven-Plugins konfigurieren
  • neue Plugins einbinden
  • Maven Profile vorbereiten, damit wir zukünftig zwischen schnell laufenden Unit-Tests und langwierigeren Integrationstests unterscheiden können.

Anpassungen unter <project><build><plugins>

Folgende Ergänzungen zur bestehenden Konfiguration in der pom.xml sind erforderlich:

<plugin>-Abschnitt für spring-boot-maven-plugin anpassen

Der <plugin>-Abschnitt der artifactId von spring-boot-maven-plugin muss wie folgt lauten (Kann zwischen dem betreffenden <plugin> und </plugin> ersetzt werden):

<plugin>-Abschnitt für maven-surefire-plugin einfügen

Das Surefire-Plugin ist für das Ausführen der Unittests in der Maven-test-Phase verantwortlich. Die Einstellungen hier sorgen dafür, dass entweder Unittests oder Integrationstests (dazu später mehr) ausgeführt werden:

  • Es werden keine Unit-Tests ausgeführt, wenn die skip.unit.test Flag gesetzt ist. Diese Flag nutzen wir später, wenn wir nur Integrationstests ausführen wollen (<skipTests>...).
  • Es werden in dieser Phase keine Integrationstests ausgeführt, sofern man sich an die Konvention hält, dass deren Namen dem Muster IT*.java genügen (<excludes>...).
<plugin>-Abschnitt für jacoco-maven-plugin einfügen

Als nächsten müssen wir das eigentliche Code-Coverage-Plugin Jacoco einbinden. Eigentlich wäre das mit den ersten drei Tags erledigt (<groupId><artifactId><version>). Wir wollen aber zwei Dinge erreichen:

  • Die Code-Coverage-Reports sollen automatisch erstellt werden, nachdem die Tests durchgelaufen sind. Es muss also ein Pfad festgelegt werden, an dem die Daten der Tests gespeichert werden (<destfile>... bzw. beim Auslesen später: <datafile>...). Zudem muss ein Ort festgelegt werden, an dem der Report dann gespeichert werden soll (<outputDirectory>...). Wir nutzen jeweils Maven-Variablen für die Pfade.

  • Die Ausgabe der Unit-Tests soll später von denen der Integrationstests getrennt werden. Daher fügen wir hier allen Unittest-Reports ein “-ut”-Suffix an (Beispiel: jacoco-ut.exec)

Im Ganzen sieht dieser Teil dann so aus:

<profiles>-Abschnitt zur Unterscheidung von Integrationstests und Unittests einfügen

Ganz am Ende der pom.xml, zwischen </build> und </project>, muss noch eine neuer Abschnitt ergänzt werden. Wir erstellen zwei Profil: einen für die Unit-Tests (<id>dev</id>) und einen für die Intergrationstests (<id>integration-test</id>).

Folgendes muss eingefügt werden:

VSCode-Addons installieren

Um die Ergebnisse in VSCode direkt darstellen zu können gibt es zwei nützliche Addons:

Coverage Gutters zeigt direkt im Code an, welche Zeilen von den Tests erfasst werden und welche nicht. VSCode Addon Coverage Gutters

LivePreview ermöglicht es, die Berichte in HTML direkt in VSCode anzusehen.

Live Preview
Live Preview

Damit sollte für’s erste das Handwerkszeug vorhanden sein. Starten wir einen ersten Versuch:

Erste Versuche: Testen und Testabdeckung ermitteln

Um die Abdeckung ermitteln zu lassen müssen wir die Tests auf bekanntem Weg ausführen, also z.B::

  • Im Projektexplorer im Abschnitt “Java Projekts” gibt es ein Play-Symbol neben dem Java-Filenamen des Tests

  • Wir rufen das entsprechende Maven-Goal auf: führe alles aus bis inklusive der Unittestphase mit allen Tests: shell mvn test -f ".\pom.xml"

  • Im Menü unter: Anzeigen/Testen findet sich ein Play-Symbol für die Tests (und später auch das Testergebnis)

Testergebnisse unter Anzeigen/Testen
Testergebnisse unter Anzeigen/Testen

Wo finde ich die Coverage-Ergebnisse?

Die Ergebnisse der Testabdeckung werden im Projektverzeichnis als HTML-Bericht aufbereitet und unter /target/site/jacoco-ut gespeichert. Diese Dateien lassen sich in jedem Browser öffnen.

Ergebnisse der Abdeckungstests im Explorer-Baum
Ergebnisse der Abdeckungstests im Explorer-Baum

Durch das installierte “Live Preview”-Addon in VSCode können wir aber direkt per Rechtsklick auf “index.html” und “Show Preview” den Bericht öffnen:

Ergebnisse der Abdeckungstests im Explorer-Baum
Ergebnisse der Abdeckungstests im Explorer-Baum

Wir erhalten zunächst eine Übersicht der obersten Ebene unseres Projekts mit klickbaren Links zur Navigation zu den einzelnen Packages:

Übersicht der Testabdeckung des Projekts
Übersicht der Testabdeckung des Projekts

Auf den Seiten der einzelnen Packages finden sich Übersichten zu den Klassen in diesen Packages versehen mit Links, um zu den Ergebnisseiten der Klassentests zu navigieren:

Testabdeckung der Klassen eines Package
Testabdeckung der Klassen eines Package

Auf der Seite einer Klasse schießlich findet sich die Übersich zu einzelnen Methoden, auch diese sind anklickbar:

Übersicht der Testabdeckung des Projekts
Übersicht der Testabdeckung des Projekts

Auf den Methdenseiten kann man Codezeilengenau sehen, welche Zeilen von den Tests erreicht wurden:

Übersicht der Testabdeckung des Projekts
Übersicht der Testabdeckung des Projekts

Interpretation der Abdeckungswerte

Wofür aber stehen die einzelnen Werte und welche Relevanz haben sie? Wir schauen uns die Ergebnisseite noch einmal Spalte für Spalte an:

Übersicht der Testabdeckung des Projekts
Übersicht der Testabdeckung des Projekts

Es wird dort ausgegeben:3

  • Missed Instructions Cov.: Anweisungsüberdeckungsgrad (C0): wie viel Prozent der insgesamt vohandenen Anweisungen werden durch die Tests ausgeführt?

  • Missed Branches Cov.: Zweigüberdeckungsgrad (C1): wie viele Prozent der Kanten von Verzweigungen des Kontrollflusses durch die Tests durchlaufen werden.

  • Missed Cxty: Zyklomatische Komplexität: Anzahl der linear unabhängigen Zweige in einem Kontrollfluß - eine Maßzahl zur Mindestanzahl an nötigen Testfällen. Unter “Missed” steht die Anzahl der nicht erreichten Verzweigungen)

  • Missed Lines: Zeilenüberdeckungsgrad: Anzahl der erreichten und nicht erreichten (Missed) Quellcodezeilen (in zwei Spalten)

  • Missed Methods : Methodenüberdeckung: Anzahl der erreichten und nicht erreichten (Missed) Methoden (in zwei Spalten)

  • Missed Classes: Klassenüberdeckung: Anzahl der erreichten und nicht erreichten (Missed) Klassen (in zwei Spalten, hier nicht zu sehen, weil bereits auf Klassenebene)

Überdeckungsgrade

Die Überdeckungsgrade geben an, wie viele Anweisungen, Zweige usw. von den Tests durchlaufen werden. Hintergründe zu den Überdeckungsmetriken finden sich in diesem Artikel.

Zyklomatische Komplexität

Die Zyklomatische Komplexität stellt ein Maß für die Wartbarkeit und Testbarkeit von Code dar. Die Zahl repräsentiert die Anzahl (linear) unabhängiger Pfade, entlang derer ein Codeblock durchschritten werden kann. Eine hohe Zyklomatische Komplexität bedeutet, dass zumeist auch viele Testfälle nötig sind und der Code schlecht erweiterbar und wartbar ist. Ab 10 gilt eine Methode als komplex, ab 20 ab sehr schwer zu testen. Hintergründe finden sich in diesem Artikel

Zeilenüberdeckungsgrad

Wieviele Quellcode-Zeilen wurden vom Test ausgeführt? Es sind nicht die Anweisungen, sondern die Programmzeilen relevant, z.B. bei if-Statements kommt es auf die Formatierung an (einzeilig oder mehrzeilig). Der Vorteil ist: Debugger liefert i.d.R. die Zahl der durchlaufenen Zeilen, daher einfach umzusetzen. Diese Kennziffer ist allerdings weniger aussagekräftig als der Anweisungsüberdeckungsgrad.

Methodenüberdeckung

Wie viele Methoden wurden von keinem Test aufgerufen. Diese Zahl ist unabhängig von der jeweiligen Anweisungs- und Zweigüberdeckung - und daher wenig aussagekräftig für die Testqualität. Trotzdem sollte geprüft werden, welche Methoden nicht getestet werden - und ggf. nachgebessert werden.

Klassenüberdeckung

Wie viele Klassen wurden von keinem der Tests genutzt. Analog zur Methodenüberdeckung kann diese Zahl eher dazu dienen, systematisch bislang ungetestete Codesequenzen zu finden. Als Maß der Codegüte dient sie nicht, da Klassen hier als “getestet” erscheinen, sobald der Test auch nur eine Codezeile darin ausführt.

Fazit und Nutzen

Auf Basis der so gewonnenen Erkenntnisse können weitere sinnvolle Testfälle ermittelt werden - oder toter Code entfernt werden.

Es gibt kein allgemeingültiges Mindestmaß an Codeabdeckung. Die jeweils erforderliche Testabdeckung sollte in Anbetracht des Anwendungsfalls nach einer Risikoabschätzung sowie der eigenen Fähigkeiten getroffen werden. Einen guten Rahmen gibt die Testivus -Geschichte “How much unit test coverage do you need” von Alberto Savoia wieder.

Nächste Schritte

Dieser Artikel ist ein Teil der Artikelserie zu einem Adressbuch-SpringBoot-Projekt. Weiter geht es zu gegebenem Zeitpunkt._


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY-SA 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: Das Testframework JaCoCo einbinden: Metriken der Codeabdeckung” von Hannes Stein, Lizenz: CC BY-SA 4.0. Der Artikel wurde unter https://oer-informatik.de/sbb07_jacoco_codeabdeckung veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/java-springboot/Backend. Stand: 04.09.2023.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]


  1. Homepage von Jacoco / Eclemma: https://www.eclemma.org/jacoco/

  2. Creating Code Coverage Reports for Unit and Integration Tests With the JaCoCo Maven Plugin

  3. Genaue Angaben über die Spalten finden sich in der Dokumentation unter https://www.jacoco.org/jacoco/trunk/doc/counters.html

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: