Cover your world!

Codecoverage mit Clover

Autor:
Sabine Winkler
Orientation in Objects GmbH
Sabine Winkler
Sabine Winkler
Datum:April 2003

Mit der Definition von Glenford J. Myers "Testen ist der Prozess, ein Programm mit der Absicht auszuführen, Fehler zu finden." wurde jeder, der sich mit der Thematik des Testens auseinandersetzt, schon einmal konfrontiert. JUnit stellt für die Java sprechende Welt ein Framework zur Verfügung, welches das Testen in einem komfortablen, leicht zu bedienenden Rahmen ermöglicht. Geht man von einer Unit aus, einer kleinen Einheit, die in Java in erster Linie die Klasse darstellt, ist die zu testende Funktionalität relativ begrenzt und somit die Menge der zu erstellenden Tests überschaubar. Handelt es sich allerdings um ein umfangreiches Projekt mit vielen Entwicklern, stellt es sich als Problem dar, darüber Auskunft zu geben, was bereits betestet wurde und was nicht. An dieser Stelle versucht eine Messtechnik Hilfestellung zu geben - Code Coverage.

Code Coverage

Code Coverage, auch Test Coverage genannt, ist ein Verfahren zum Messen von durch Tests ausgeführtem Code. Diese Funktionalität erweist sich aus verschiedenen Aspekten als relevant. Dem Tester gibt sie die Möglichkeit, Programmcode ausfindig zu machen, der nicht betestet wird. Gleichzeitig können durch verschiedene Coverage Metriken Ergebnisse präsentiert werden, die eventuell die Sichtweise auf das Testdesign verändern. Aus Managementsicht sind vorallem Fakten wichtig. Schnell lässt sich durch Code Coverage eine Zahl ermitteln, die das Verhältnis von getestetem Programmcode zum Gesamtquellcode ausdrückt.

Allgemein versteht sich Code Coverage als Mittel zur Qualitätssteigerung von Tests. Dabei kann jedoch keine Aussage über die Richtigkeit der Tests, die Vollständigkeit des Programmcodes oder die Erfüllung von Anforderungen getroffen werden. Die Code Coverage Analyse, d.h. die Auswertung der Coverage Ergebnisse, stellt eine strukturierte Testtechnik dar. Die Basis ist der White-Box-Testansatz. Ziel strukturierten Testens ist es, das Verhalten des Programms mit dem aus dem Programmcode ersichtlichen Zweck zu vergleichen und mögliche Stolpersteine besonders miteinzubeziehen. Im Vergleich zum Funktionaltest, wo das interne Verhalten keine Beachtung findet und anhand von Programmanforderungen (z. B. Spezifikation) getestet wird, erscheint das strukturierte Testen qualitativ unterlegen.
Wie soll es möglich sein, in Kombination von funktionalen Tests (JUnit) und Code Coverage die Vollständigkeit von Programmcode sicherzustellen?

Hier kann man die Sichtweise zum Einsatz von Testechniken auf Code Coverage Analyse übernehmen. Es gibt nicht die eine Technik, die allen Anforderungen zur Qualitätssicherung eines Programms durch Testen gerecht wird. Genauso ist der Einsatz von Code Coverage Analyse zu verstehen. Aus Sicht der Qualitätssicherung muss das Optimum von Testfällen gefunden werden, um den Programmcode zu untersuchen, Fehler zu finden und dabei einen wirtschaftlichen Rahmen einzuhalten. Bei dieser Suche kann das Arbeiten mit Code Coverage Unterstützung liefern.

Code Coverage Metriken

Es gibt eine Vielzahl von Betrachtungsmöglichkeiten zu Coverage Metriken. Im folgenden werden die bekanntesten vorgestellt. Einen detailierteren Überblick bietet der Artikel " Software Negligence and Testing Coverage " von Cem Kaner.

Line Coverage (Statement Coverage)

Line Coverage misst, wieviel Zeilen oder Prozent von Programmcode durch Tests ausgeführt wurden. Es wird der theoretische Ansatz der Anweisungsüberdeckung verfolgt. Ziel ist es, dass jede Anweisung einmal durchlaufen wird. Das Zusammenfassen mehrerer Anweisungen zu einer Einheit (z. B. Anweisungen innerhalb einer Schleife), die keinerlei Verzweigungen im Sinne von if-else Anweisungen enthält, ist bei Line Coverage möglich und wird als Block Coverage bezeichnet. Line Coverage bietet sich an, um einen ersten Überblick zu erhalten, wieviel Programmcode durch Tests durchlaufen wird.

Die folgende Abbildung 1) zeigt an einem kleinen Beispiel, wie schnell eine hohe Prozentzahl an abgedeckten Statements in keinerlei Bezug zur Vollständigkeit des Tests steht.

Die Grenzen von Line Coverage

Abbildung 1) Line Coverage und die Grenzen der Aussagekraft

Branch Coverage

Mit Branch Coverage wird die Überdeckungsmessung etwas feiner in der Granularität in ihrer Betrachtung. Da, wie in Abbildung 1), manche Anweisungen nur unter bestimmten Bedingungen ausgeführt werden dürfen, müssen entsprechende Testfälle alle Möglichkeiten durchspielen. Branch Coverage oder Kantenüberdeckung bedient sich dabei eines anschaulichen Hilfsmittels, dem Kontrollflussgraphen, um die Kontrollstruktur des Programms darzustellen. Ein Kontrollflussgraph ist ein gerichteter Graph aus einer endlichen Menge von Knoten, welche alle für jeweils eine ausführbare Anweisung stehen. Abbildung 2 zeigt ein Beispiel für die Notation eines Programmcodes als Kontrollflussgraphen.

Verschiedene Beispiele für die Notation von Kontrollflussgraphen

Abbildung 2) Verschiedene Beispiele für die Notation von Kontrollflussgraphen

Ziel von Branch Coverage ist es, dass jede Kante des Graphen von Tests durchlaufen wird. In Abbildung 3 wird dies an einem kleinen Beispiel veranschaulicht und der Unterschied zwischen Line Coverage und Branch Coverage kontrastiert.

Branch Coverage

Abbildung 3) Branch Coverage

Decision Coverage

Eine erweiterte Sichtweise von Branch Coverage stellt die Bedingungsüberdeckung (Decision Coverage) dar. Um diesem Ansatz aus Testsicht genüge zu leisten, müssen die Testfälle nicht nur jede Kante durchlaufen, sondern auch jede Bedingung in allen möglichen Kombinationen durchspielen. Erweitert man das Beispiel in Abbildung 3 um eine zweite Bedingung in der if-Anweisungen, if (value < 0 && con1) wobei con1 vom Typ boolean ist, ändert sich am Ergebnis von 100 % Branch Coverage nichts. Nach dem Ansatz der Bedingungsüberdeckung sind hier jedoch nur 50 % aller möglichen Testszenarien erfolgt. Jede Kante muss so durchlaufen werden, dass jede Bedingung für sich genommen entweder erfüllt wird oder nicht. Desweiteren müssen jeweils beide Bedingungen gemeinsam zutreffen oder gemeinsam nicht erfüllt sein. Dies bedeutet eine Verdoppelung in diesem Beispiel der Testfälle:

assertEquals(10, covered.doSomething(-10, true));
// true - true
assertEquals(10, covered.doSomething(10, true));
// false - true
assertEquals(10, covered.doSomething(-10, false));
// true - false
assertEquals(10, covered.doSomething(10, false));
// false - false

Path Coverage

Path Coverage verfolgt das Ziel, jeden möglichen Pfad in jeder Funktion oder Methode zu durchlaufen. Dabei stellt ein Pfad eine einmalige Sequence von Kanten dar, beginnend beim Anfang der Methode bis zu ihrem Ende. Auf den ersten Blick wird kein Unterschied deutlich zum vorangegangenen Beispiel (Abbildung 3) ). Alle Kanten werden mit allen möglichen Kombinationen von Bedingungen durchlaufen. Jedoch stellt eine Kante nicht gleichzeitig einen Pfad dar, vielmehr ist sie Bestandteil davon. Hier zeigt sich eines von zwei grundlegenden Problemen von Path Coverage. Einige Kombinationen von Kanten, wie zum Beispiel Abbildung 4) illustriert, sind unmöglich gemeinsam ausführbar. Gleichzeitig stellen sie aber im Theoretischen zu betestende Pfade dar. Wie also findet man die "wirklich" zu betestenden Pfade ?

Path Coverage

Abbildung 4) Path Coverage

Ein weiteres Problem von Path Coverage stellt die mit Anzahl der Kanten exponentiell steigende Menge von Pfaden dar. Die Testmenge wächst mit jeder neu hinzukommenden Kante erheblich an. Ein Erreichen von 100 % Pfadüberdeckung ist nur schwer nicht möglich.

Clover your world !

Nach dem Betrachten der Theorie von Code Coverage, soll mit dem Produkt Clover ein Tool vorgestellt werden, welches es ermöglicht, Programmcode zu "durchleuchten". Das von der Firma Cortex hergestellte Tool ist für die Arbeit mit JUnit entwickelt worden. Mit einem eigenen Compiler wird der zu covernde Programmcode übersetzt. Danach werden die Tests ausgeführt. Auf Basis der vorher durch den Compile Vorgang gespeicherten Daten können die Coverage Ergebnisse in verschiedenen Formaten, wie z. B. HTML präsentiert werden (Abbildung 5 ). Clover basiert auf dem theoretischen Ansatz von Branch Coverage.

Clover Ergebnisse im HTML Format

Abbildung 5) Clover Ergebnisse im HTML Format

Was macht Clover interessant ?

Clover ist leicht in bestehende Projekte mittels IDE (z.B. Eclipse und IntelliJ) oder Ant integrierbar! - Die entsprechenden Files, z.B. eine Clover-Ant Distribution stehen direkt auf der Homepage von Clover zum Download zur Verfügung und können als Trial Version zu zu Testzwecken verwendet werden. Bei der Verwendung mit Ant wird der Coverage Mechanismus über entsprechende Tasks angestossen. Deren Anwendung sowie alle weiteren Informationen zum Einsatz werden sehr ausführlich in der Clover Documentation beschrieben. Ein Projekt, das mit einem automatisierten Ant Build Prozess arbeitet, kann in kürzester Zeit auch Code Coverage einsetzen und damit Tests überwachen.

Clover erlaubt modifizierbare Betrachtung der Coverage Ergebnisse !- Grundlegend liefert jeder der von Clover unterstützten Formate (HTML, XML, Emacs Style, PDF) Auskunft darüber, wie oft welche Zeilen durchlaufen wurden und lässt schnell durch farbige Markierung erkennen, wo beispielsweise kein Testcode den Programmcode betreten hat. Gleichzeitig liefert es prozentuale Angaben zu durchlaufenen Methoden, Anweisungen und Bedingungen und berechnet die Gesamt-Coverage Rate. Eine mitgelieferte Swing Oberfläche (Abbildung 6) erweitertet die Betrachtungsmöglichkeiten. Über Filter können wahlweise nur einzelne Elemente (z. B. nur Bedingungen) betrachtet und andere Elemente von der Betrachtung ausgeschlossen (z. B. Vernachlässigung von try - catch - Blöcken) werden. Die Prozentzahlen werden nachberechnet.

Was Clover zu bemängeln lässt, ist beispielsweise ein fehlender Filter, um eine getrennte Betrachtung von ausgeführten public Methoden und ausgeführten private Methoden zu erhalten. (Trennung von funktionaler und strukturierter Sichtweise auf Tests)

Die Swing Oberfläche von Clover mit Filtern

Abbildung 6) Die Swing Oberfläche von Clover mit Filtern

Clover unterstützt OpenSource Entwicklung !- Für einen im Vergleich geringen Obolus kann man eine lizensierte Version erwerben. Handelt es sich beim gewünschten Einsatzgebiet um ein Open Source Projekt, erhält man von Cortex eine kostenlose Version.

Wie Clover in ein Projekt integriert werden kann, können Sie sich an dem von unserem Haus entwickelten Open Source Projekt Bugkilla (http://bugkilla.sourceforge.net/) selbst ansehen. Bugkilla ist eine Sammlung von Werkzeugen zur Unterstützung des funktionalen Testens von J2EE Web Anwendungen. Das Hauptziel ist die Automatisierung der Spezifikation und Ausführung von Tests in ein und demselben Werkzeug. Das Projekt hat eine Unterstützung von Clover bereits in seiner build.xml (http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/bugkilla/bugkilla/build.xml) integriert. Bugkilla wird mit existierenden Werkzeugen und Framworks integriert, so basiert beispielsweise der Player zur Testdurchführung auf junit, auch ein Eclipse Plugin (http://sourceforge.net/project/showfiles.php?group_id=60208 )steht bereits zur Verfügung.

Die Suche nach der Zahl

Code Coverage, wie bereits in der Einleitung erwähnt, ist kein Garant für die Korrekheit und Vollständigkeit von Code. Es geht vielmehr darum, aufzuzeigen, wieviel und wie gründlich der Code abgearbeitet wurde. Natürlich kann somit eine 100 % Abdeckung keine Gewährleistung für das Ende der Testarbeiten geben. Allerdings scheinen 85% Coverage nach Beschau der gängigen Literatur ein vernünftiges Maß von Überdeckung darzustellen. Nach den vorangegangenen Ausführungen muss zwangsläufig die Frage auftauchen, woher dieser Wert stammt. Eine vielleicht nicht beruhigende aber wahrscheinlich der Wahrheit sehr nahe Antwort findet Brian Marrick:

"85% is a common number. People seem to pick it because that's the number other respectable companies use. I once asked someone from one of those other respectable companies why they used 85%. He said, "When our division started using coverage, we needed a number. Division X has a good reputation, so we thought we'd use the number they use." I didn't follow the trail back through Division X. I have the horrible feeling that, if I traced it all the way back to the Dawn of Time, I'd find someone who pulled 85% out of a hat ."

Welche Rückschlüsse die Coverage Rate über das Programm und deren Tests zulässt, ist somit diskussionswürdig. Verliert Code Coverage damit Relevanz ? - Nein, es liefert Meßwerte, um z. B. Fortschritte in Projekten zu messen, erste Eindrücke zu erhalten, wo sich ein Projekt befindet. Es deckt Stellen auf, die noch nicht von Testcode durchlaufen werden. Der Einsatz von Coverage kann ebenso Erweiterung in anderen Gebieten finden. Zur Unterstützung von Performanz Optimierung kann man mittels Coverage herausfiltern, welche Codezeilen oder -blöcke oft abgearbeitet werden, um an diesen Stellen Optimierung vorzunehmen. Sogar die etwas weit hergeholte Idee, ein fertiges Produkt zu "covern", ist möglich. Man kann beispielsweise den "gecoverten" Programmcode starten, es wie gewohnt verwenden und sich nach einiger Zeit die Ergebnisse als Auskunft über evtl. überflüssigen Programmcode geben lassen.
Wahrscheinlich gibt es noch viel mehr zu "entdecken" - Cover your world !

Links

Cem Kaner - Software Negligence and Testing Coverage
(http://www.kaner.com/coverage.htm)

Brian Marrick - How to misuse Code Coverage
(http://www.testing.com/writings/coverage.pdf)

JUnit
(http://www.junit.org)

Clover Coverage Tool for Java
(http://www.atlassian.com/software/clover/)

The Ant User Manual
(http://ant.apache.org/manual/)

The Bugkilla Project
(http://sourceforge.net/projects/bugkilla/)

Zum Geschaeftsbreich Competence Center
Schulungen
Mehr zu Code Coverage, Softwaretests und Qualitätssicherung in der Schulung automatisiertes Testen mit Java.