Im ersten Teil habe ich die Grundlagen von Komponententests (auch Unit-Tests genannt) erklärt. Diese werden in der Form (und noch vielfältiger) in der Praxis auch so angewandt. Mit dem heutigen Artikel wollen wir die Praxis langsam verlassen und uns langsam in den Bereich der akademischen Forschung begeben – was nicht heißt, dass der in diesem Artikel vorgestellte Ansatz nicht auch in der Praxis angewendet wird; nur eben deutlich seltener (sollte sich unter meinen Lesern jemand befinden, der die hier beschriebene Technik tatsächlich in der Praxis anwendet, würde ich mich über eine kurze Meldung in den Kommentaren freuen).
Also, worum geht es?
Wie im letzten Artikel vorgestellt, können Tests oder Testmethoden verwendet werden, um einzelne Komponenten eines Programms auf Fehler hin zu untersuchen. Schlägt ein solcher Test fehl, liegt die Vermutung nah, dass die entsprechende Komponente einen Fehler enthält. Sind die Komponenten entsprechend klein gewählt, kann der Fehler somit relativ genau im Programmcode eingegrenzt werden. Stellen die Komponenten also etwa einzelne Methoden im Programm dar – man spricht auch von der Methodenebene als Granularität -, lässt sich ein potentieller Fehlerort auf eine einzelne Methode eingrenzen.
Soweit zumindest die Theorie. In der Praxis scheitert diese Idealvorstellung daran, dass derart feingranulare Komponenten selten komplett unabhängig von anderen getestet werden können. So rufen etwa Funktion während ihrer Abarbeitung meist auch andere Funktionen auf, die wiederum weitere Funktionen aufrufen können und so weiter. Ein fehlschlagender Test weist demzufolge lediglich darauf hin, dass irgendeine der durch ihn direkt oder indirekt aufgerufenen Funktionen einen Fehler enthält (und manchmal ist nicht einmal das sicher – aber dazu vielleicht in einem späteren Artikel mehr). Um dennoch auf Grundlage fehlschlagender Tests einen Fehlerort im Programm bestimmen zu können, kann man sich der Technik der Fehlerlokalisierung (Fault Localization in der Fachliteratur) bedienen.
Ein wichtiger Begriff in diesem Zusammenhang ist der der Abdeckungsmatrix. Wie erwähnt, werden durch einen Test in der Regel mehrere Funktionen direkt oder indirekt aufgerufen. Wird während der Testausführung überwacht, welcher Test welche Funktionen aufruft, können die so gesammelten Informationen in einer Matrix – eben der Abdeckungsmatrix – zusammengefasst werden. Diese hat üblicherweise eine Spalte für jeden ausgeführten Test und eine Zeile für jede ausgeführte Methode (oder was auch immer als Komponente aufgefasst wird; verbreitet sind Methoden und Statements). Eine “1” in einer Zelle der Matrix bedeutet, dass der Test dieser Spalte die entsprechende Methode (direkt oder indirekt) aufruft; eine “0” dementsprechend, dass der Test diese Methode nicht aufruft. Eine derartige Matrix könnte zum Beispiel so aussehen (die 0en wurden der Übersichtlichkeit halber weggelassen):
t1 | t2 | t3 | t4 | |
m1 | 1 | 1 | ||
m2 | 1 | 1 | 1 | |
m3 | 1 | 1 | ||
m4 | 1 | 1 | 1 |
Diese Matrix sagt uns, dass der Test t1 die Methoden m1, m2 und m4 aufruft, der Test t2 die Methoden m2 und m3 und so weiter. Schlägt nun t1 fehlt, müsste sich der Fehler irgendwo in m1, m2 und m4 finden lassen – wo genau, lässt sich aus der Matrix aber natürlich nicht direkt ablesen.
Hier kommt nun allerdings die Technik der Fehlerlokalisierung – oder, genauer: die der abdeckungsbasierten Fehlerlokalisierung – ins Spiel. Schlägt nämlich mehr als ein Test fehl, kann aus dem Verhältnis aus fehlgeschlagenen und fehlerfreien Tests abgeschätzt werden, wo sich der Fehler ungefähr im Programm befinden könnte. Dazu wird mit einem Fehlerlokator ein Ranking aller aufgerufenen Methoden bestimmt; die Methoden mit der höchsten Fehlerwahrscheinlichkeit befinden sich im Ranking oben, die anderen unten.
Ein in der Forschung häufig zitierter Fehlerlokator ist der Tarantula-Lokator (über die Herkunft des Namens müsste ich spekulieren, würde aber meinen, dass er nach einer Spinne benannt wurde, weil er in den Spider Labs der University of California entwickelt wurde; letztere haben vermutlich ihren Namen daher, dass sie sich mit der Bug-Suche beschäftigen…aber das ist alles Spekulation). Dieser Lokator berechnet das Ranking, indem jeder Methode ein Verdächtigkeitswert zugeordnet wird – je verdächtiger eine Methode ist, desto wahrscheinlicher enthält sie einen Fehler. Die Verdächtigkeit einer Methode errechnet sich aus dem Verhältnis von fehlschlagenden und erfolgreichen Tests, von denen sie aufgerufen wird; konkret sieht die Formel so aus:
Kommentare (12)