Suchen

Das Testen von Software - Ein Paradoxon

| Autor / Redakteur: Chris Hobbs* / Martina Hafner

Zum Kern des Software-Testens gehört ein Paradoxon. Eine mögliche Lösung dieses Paradoxons, die hiermit erklärt wird, hat wichtige Folgen für den automatisch generierten Code und die Testfallerzeugung.

Firmen zum Thema

Abb. 1: Beispiel von Kombinationen
Abb. 1: Beispiel von Kombinationen
(Bild: QNX Software Systems)

Das Paradoxon selbst ist schon beschrieben worden [7] und wird hier nur zusammengefasst. Es ist allgemein anerkannt, dass dynamisches Testen nur einen winzigen Teil des Zustandsraums eines software-basierten Systems abdecken kann. Zum Beispiel, Absatz 5.4 von IEC29119 [1] behauptet, »Es ist unmöglich, ein Softwaresystem 100% zu prüfen, deshalb ist Prüfen nur ein Probenahme«. In der Tat ist nicht nur 100% sondern auch 0,000000001% Testabdeckung unerreichbar.

Angesichts dieser Tatsache, könnte man die Idee, dass das Testen nutzlos sei, unterstützen. Warum verschwendet eine Firma Geld und Zeit, um einen verschwindend kleinen Teil des Zustandsraums eines Systems zu testen?

Trotz dieser Beschränkungen erweisen sich Tests dennoch wirksam: In der Tat decken solche Tests echte Probleme auf.

Dieses Papier wiederholt in verkürzter Form das Paradoxon, das in Referenz [7] erwähnt wird, und bietet eine mögliche Erklärung, die zeigt, wie man die Verifikation von sicherheitskritischen Systemen verbessern könnte. Sie bietet auch eine Warnung gegenüber den Erzeugungsmethoden von Code und Testfällen an, die wir zurzeit erproben.

Die Unmöglichkeit des erschöpfenden Tests

Das folgende Programm berechnet 2+2 und illustriert das Problem:

int main()

{

int x = 2 + 2;

printf(“2 + 2 = %d\n”, x);

return 0;

}

Um dieses Programm testen zu können, muss man die printf() Funktion verstehen. In den meisten Realisierungen dieser libc Funktions, muss printf() die Mutex sperren, die stdout schützt. Deshalb muss man Testfälle erzeugen, die das Programm kontrollieren, wenn zum Beispiel die Mutex zur Verfügung steht, ebenfalls wenn sie schon gesperrt ist, ebenfalls wenn das Programm, das sie gesperrt hat, gestorben ist, ebenfalls wenn ein Interrupt zwischen Mutexreservierung und Mutexfreisetzung vorkommt, usw. Dieses Programm, in Zusammenhang mit dem Betriebssystem (OS), auf dem es läuft, hat ein Zustandsraum von mindestens 10100 Zustände. Es wäre auch schwierig, während des Testes diese genauen Situationen einzurichten.

Auch wenn man je 1.000.000 Tests pro Sekunde durchführen könnte, so würde die Untersuchung viel länger als das Alter des Universums dauern.

ISO 29119

Teile 1 bis 3 von ISO 29119 wurden 2013 herausgegeben; wir warten auf den vierten Teil (»Test Techniques«). Die Autoren von ISO 29119 erkannten das Problem der Kosten/Ergebnis Relation des Testens, und schlugen sogenannte »risk-based« (das heißt risikobasierte) Tests vor. Diese Philosophie verändert den Software-Test, der vorher fast ausschließlich auf Anforderungs- und Stresstests basierte. Da es unmöglich ist, ein Softwaresystem vollständig zu untersuchen, muss eine Firma die genauen kommerziellen (statt technischen) Rechtfertigungen der Tests identifizieren.

ISO 29119 enthält verschiedene Beispiele von solchen Risiken: rechtliche oder vertragliche Anforderungen werden nicht erfüllt, der Projektabschluss wird verzögert, usw.

Testen als »Vertrauen durch Anwendung«

Die Beobachtung, dass die Tests nie vollständig ausgeführt werden können, in Zusammenhang mit den Aussagen von ISO 29119, führt zu einem neuen Verständnis des Testkonzepts. Referenz [7] stellt die Idee vor, dass der Zweck des dynamischen Tests darin liegt, »Vertrauen in Erfahrungswerte aus der Anwendung« zu stärken.

Sicherheitsnormen wie IEC 61508 und ISO 26262 beschreiben das Konzept von »Proven-in-use« (das heißt, Der Beweis durch Anwendung), aber Vertrauen durch Anwendung ist eine genauerer Begriff, weil die Anwendung die Sicherheit eines Systems niemals beweisen kann.

Was macht ein Tester? Spielt er eine Art von digitaler Homöopathie, wodurch er Testfälle zufällig aus dem Zustandsraum auswählt? Natürlich nicht. Selbst wenn er solche Zustände auswählte, so wäre es unmöglich, die Anfangsbedingungen der Tests herzustellen.

Stattdessen analysiert man das System, versteht die definierten Risiken, die reduziert werden müssen, und nutzt seine eigenen Erfahrungen, um wirksame Tests auszuwählen. Für das oben geschriebene (2+2) Programm, würde man viele Instanzen des Programms gleichzeitig ausführen, um Konkurenzsituationen für die Mutex zu erzeugen.

Wenn diese Tests durchgeführt werden, ohne Probleme zu finden, so steigt unser Vertrauen in das System. Aber die schwierige Frage bleibt: »Wie kann die Auswahl von einigen hundert Tests aus einer Menge, die vielleicht 10100 Tests enthält, unser Vertrauen merklich erhöhen?« Wenn man diese Frage nicht beantworten kann, so ist es schwierig, die Anwendung von Tests als Nachweis für sicherheitskritische Systeme zu rechtfertigen.

Eine mögliche Antwort liegt in den Beobachtungen, auf welcher Combinatorial Testing (Kombinatorisches Testen) basiert.

Eine mögliche Lösung des Paradoxons

Kombinatorisches Testen basiert auf der empirischen Bemerkung, dass es so scheint, als ob das Systemversagen von den Wechselwirkungen zwischen wenigen Variabeln abhängt. Deshalb können Tests, die solche Kombinationen von wenigen Variabeln umfassen, sehr effektiv sein [3].

Zum Beispiel, wenn es um ein System geht, das von 20 binären Variabeln abhängt, so muss man 220 = 1.048.576 Testfälle durchführen, um das System vollständig zu testen. Aber um alle Wertekombinationen von jedem fünften der 20 Variabeln zu testen, braucht man nur 118 Testfälle und, empirisch bemerkt, eine solche Testmenge genügt.

Leider gibt es keine geschlossene Lösung, die die verlangte Anzahl von Testfällen für bestimmte Werte berechnet, aber sie hat Ordnung

vt log(n)

worin ν die Anzahl von möglichen Variabelwerten ist (2 (binär) im oben gegebenen Beispiel), t die Wechselwirkungsstärke ist (5 im Beispiel) und n die Anzahl von Variabeln darstellt (hier 20). Obwohl keine geschlossene Formel zur Verfügung steht, enthält [4] eine Liste der kleinsten Anzahl von bestimmten Kombinationen, die bisher bekannt sind.

Abbildung 1 bietet ein Beispiel von kombinatorisch erzeugten Parametern. Für dieses Beispiel gibt es fünf Parameter (0 - 4). Der erste ist boolesch (F/W), die anderen vier nehmen Werte von { 0, 1, 2 }.

Abb. 1: Beispiel von Kombinationen
Abb. 1: Beispiel von Kombinationen
(Bild: QNX Software Systems)

Insgesamt gibt es 2 x 34 = 162 Kombinationen dieser fünf Parameter. Abbildung 1 zeigt eine Menge von zehn Testfällen, die alle 2-Parameterkombinationen enthalten - das heißt wählt man irgendwelche zwei Parameter, so umfassen die zehn Testfälle alle möglichen Kombinationen dieser zwei Parameter. Die in Abbildung 1 gezeigten Testfälle wurden mit dem ACTS Tool [3] erzeugt.

Obwohl es viele empirische Daten gibt, die zeigen, dass diese begrenzten Kombinationen genügen [5], aber dennoch ohne eine theoretische Grundlage zu haben, fragt man: »warum?« Referenz [3] schlägt vorläufig vor, dass die Antwort darauf von der kleinen Anzahl von Variabeln pro Entscheidungspunkt (if, while, for, usw) abhängt.

Abbildung 2 zeigt empirische Daten des QNX Neutrino Betriebssystems. 99% der Entscheidungspunkte enthalten drei oder weniger Variabeln.

Abb. 2: Endscheidungspunkte des QNX Betriebssystems
Abb. 2: Endscheidungspunkte des QNX Betriebssystems
(Bild: QNX Softwaer Systems)

Eine mögliche Antwort auf die Frage, warum das Testen überhaupt effektiv ist, hängt von dieser Tatsache ab. Menschliche Programmierer reduzieren ohne nachzudenken die Anzahl der Kombinationen im System, um einen Entwurf erzeugen zu können. Menschliche Tester verteilen automatisch die Testkombinationen, um den Test durchführen zu können. Diese beiden Verfahren reichen glücklicherweise aus, nützliche Tests herzustellen. IEC 61508 [6] empfiehlt sogenannte »Error Guessing« (das heißt das Vermuten von Fehlern), um Testfälle zu erzeugen, und die Fehler, die menschliche Tester vermuten ('guess'), zeigen sich normalerweise in einer kleinen Anzahl von Kombinationen.

Die Folgen für automatisch generierten Code und Tests

Eine solche Lösung des Paradoxons würde auch erklären, warum automatisch generierte Tests, obwohl sie gutes Code-Coverage liefern können, selten tief verborgene Bugs entdecken. Sie erklärt auch, wie nach einer Systemveränderung, eine Teilmenge der Testfälle oft genügt, um das System effektiv wieder zu testen. Das ist auch ein Paradoxon: Sogar eine kleine Veränderung eines Softwaresystems stört den Zustandsraum erheblich. Warum genügt eine Teilmenge der Testfälle?

Diese Antwort hat aber ernsthafte Konsequenzen für automatisch generierten Code: Ist kombinatorisches Testen (ob menschlich versehentlich oder formal angewandt) wirksam genug, solchen unmenschlichen Code zu testen?

Aus anderen Gründen (zum Beispiel um die Richtigkeit unseres Entwurfs formal beweisen zu können) führen wir automatisch die Code- und Testfallgenerierung ein. Dadurch ändern wir möglicherweise die Struktur des Programms, und reduzieren die Wirksamkeit unserer Verifikation.

Zusammenfassung

Das Testen eines Softwaresystems besteht nicht mehr aus der vollständigen Prüfung aller Anforderungen - das ist heutzutage unmöglich. Selbst wenn wir wie bei IEC 29119 risikobasierte Tests im Focus haben, ist es schwierig zu erklären, warum das Testen von Nutzen ist.

Dennoch bemerken wir, dass in der Tat die menschlich erzeugten Tests tief verborgene Bugs entdecken, und die Lösung dieses Paradoxons liegt vielleicht bei der Beobachtung von Combinatorial Testing. Wenn ja, müssen wir vorsichtig sein, wenn wir automatisch generierten Code testen. Wir könnten auch unsere Prüfmethoden durch eine bewusste, statt versehentliche, Auswahl von kombinatorischen Tests verbessern.

Literaturhinweise

[1] ISO/IEC/IEEE 29119, Software and systems engineering—Software Testing, Erste Ausgabe, September 2013.

[2] ISO 13849, Safety of machinery---Safety-related parts of control systems , Zweite Ausgabe, November 2006

[3] Introduction to Combinatorial Testing, D Richard Kuhn, Raghu Kacker, Yu Lei, April 2013

[4] Covering Array Tables for t=2,3,4,5,6

http://www.public.asu.edu/~ccolbou/src/tabby/catable.html

[5] Failure Modes in Medical Device Software: An Analysis of 15 Years of Recall Data, Dolores R. Wallace and D. Richard Kuhn, 2001, http://csrc.nist.gov/staff/Kuhn/final-rqse.pdf

[6] IEC 61508, Functional safety of electrical/electronic/programmable electronic safety-related

systems , Part 3, April 2010

[7] Confidence-from-Use bei Medizinprodukten: Chris Hobbs, Elektronik Praxis, September 2014, http://www.elektronikpraxis.vogel.de/themen/embeddedsoftwareengineering/testinstallation/articles/459085

* Chris Hobbs ist Kernel-Entwickler bei QNX Software Systems. Mit freundlicher Genehmigung wurde dieser Beitrag dem Tagungsband Embedded Software Engineering Kongress 2014 entnommen.

(ID:43691678)