Testabdeckung in Python messen

Wir wissen nun, warum man testet, welche Tests es gibt und wie man mit dem unittest-Modul in Python Tests schreibt. Ganz nebenbei haben wir den testgetriebenen Ansatz (TDD) und diesen sogar bei unserem FizzBuzz-Beispielprojekt angewandt. Allerdings haben wir nicht immer den Luxus, mit einem brandneuen Projekt zu beginnen, so dass wir eventuell nicht wissen, welche Stellen im Code mit Tests gesichert sind und welche nicht. Vielleicht haben wir auch den Fall, dass wir nicht durchgehend testgetrieben entwickelt haben und nun im Nachhinein wissen möchten, welche Stellen nicht von Tests abgedeckt sind. Hier hilft das Modul „coverage“ weiter.

Was ist mit Testabdeckung gemeint?

Das englische Wort „coverage“ lässt sich mit „Abdeckung“ ins Deutsche übersetzen. Gemeint ist damit der relative Anteil der durch Tests geprüften Codezeilen im Verhältnis zu der Gesamtzahl aller Codezeilen. Die Testabdeckung – auch code coverage genannt – drückt also mit einer Prozentzahl aus, wie groß der Anteil der von Komponententests geprüften Codezeilen ist. Eine Testabdeckung von 100 % wäre perfekt, eine Abdeckung von 80 % sollte es mindestens sein.

Das Modul „coverage“ installieren

Damit wir unsere Testabdeckung messen können, müssen wir zunächst das Modul „coverage“ installieren. Das mache wir im Terminal mit dem Aufruf von

pip install coverage

Damit lädt der package installer for Python (pip) das Modul coverage und installiert es.

Die Testabdeckung ermitteln

Im Terminal navigieren wir in das Verzeichnis, wo unsere FizzBuzz-Dateien liegen, die wir beim Kennenlernen der Komponententests in Python angelegt haben. Dort ermitteln wir die Testabdeckung, indem wir folgenden Befehl ausführen:

coverage run TestFizzBuzz.py

Wir lassen damit coverage über unsere Testdatei laufen und erhalten als Ergebnis:

....

----------------------------------------------------------------------

Ran 4 tests in 0.000s

OK

Das ist erst einmal unspektakulär, denn es sieht genauso aus wie ein erfolgreicher Testdurchlauf, wie wir ihn schon kennen. Allerdings hat coverage im Verzeichnis eine Datei namens .coverage angelegt, die wir in der grafischen Oberfläche nicht sehen können. Die Eingabe von

ls -la

Im Terminal zeigt uns eine Liste der im Verzeichnis enthaltenen Dateien – inklusive der versteckten, da wird .coverage auch sichtbar. Darin ist das Ergebnis der Überprüfung vom vorherigen coverage-Durchlauf.

Nun müssen wir gar nicht so umständlich vorgehen, denn das coverage-Modul bietet eine übersichtliche Darstellung der ermittelten Testabdeckung. Führen wir im Terminal

coverage report -m

aus, so erhalten wir folgende Tabelle:

Name Stmts Miss Cover Missing

-----------------------------------------------

FizzBuzz.py 20 0 100%

TestFizzBuzz.py 27 0 100%

-----------------------------------------------

TOTAL 47 0 100%

Kaum überraschend haben wir eine vorbildliche 100-prozentige Testabdeckung. Das sollte stets unser Ideal sein.

Weitere Möglichkeiten von coverage

Der Zusatz „-m“ beim Aufruf des coverage-Berichts stellt die Spalte „Missing“ dar. In unserem Beispiel ist diese Spalte leer, aber hier würden die Zeilen stehen, die durch keine Tests abgedeckt sind.

Ist man unzufrieden mit der eher spartanischen Ansicht im Terminal, gibt es auch die Möglichkeit, die Testabdeckung in HTML auszugeben. Das bietet den weiteren Vorteil, dass man auf einem Blick sehen kann, welche Zeilen nicht durch Tests abgedeckt sind, denn diese werden rot hinterlegt.

Mit

coverage html

im Terminal veranlassen wir das Erstellen der ermittelten Testabdeckung in HTML-Form. Zwar passiert im Terminal nichts, aber im Verzeichnis gibt es nun einen Ordner mit dem Namen „htmlcov“. Wir öffnen die darin befindliche Datei „index.html“ und sehen im Browser die bereits bekannte Übersicht. Klickt man nun auf den Namen der aufgelisteten Python-Dateien, bekommt man eine sehr übersichtliche Darstellung der Testabdeckung auf Datei-Ebene.

Code Coverage HTML-Übersicht
Code Coverage HTML-Übersicht

Fazit

Auch wenn das coverage-Modul nicht perfekt ist, denn es zeigt uns beispielsweise nicht an, wenn wir bestimmte Codestellen mit Tests übersät haben, während wir andere lediglich mit einem einzelnen Test abspeisen. Für unseren Ansatz aber, mit so wenig Einsatz wie möglich maximale Ergebnisse zu erzielen, eignet sich „coverage“ bestens.