Cover your world!
Codecoverage mit Clover
Autor: | Sabine Winkler Orientation in Objects GmbH | ![]() 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.
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.
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.
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 ?
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.
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)
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/)