Funktionale Tests testen die spezifizierte Funktion von Software und werden nach den dabei verwendeten Methoden und der dazu zur Verfügung stehenden Information unterschieden.
Grundlage und Zielsetzung
Ausgehend von einer eindeutigen Spezifikation, was ein Softwaresystem leisten soll, kann auf jeder Granularitätsebene vom Modul bis hin zum gesamten System durch zunehmend komplexere Tests überprüft werden, ob die spezifizierte Funktionalität auch erfüllt wird. Dabei ist offensichtlich, dass die Überprüfung der Einhaltung einer gewünschten Beziehung zwischen Ein- und Ausgaben für einen überschaubaren Algorithmus wesentlich einfacher ist als das komplexe Gesamtverhalten eines aus vielen Komponenten bestehenden Systems mit Benutzerinteraktion. Gemeinsam ist allen Verfahren, dass mit vertretbarem Aufwand nur eine begrenzte Zahl von Testfällen getestet werden kann, die möglichst ‚repräsentativ‘ für die Menge aller möglichen Eingaben sein sollten. Dies kann durch Strukturierung der Wertebereiche für Eingabeparameter in zu gleichen Reaktionen führende Klassen ebenso erreicht werden wie durch die Beschreibung typischer erlaubter oder fehlerhafter Interaktionsfolgen für ein Dialogsystem, die aus den Anwendungsfällen früher Entwicklungsphasen abgeleitet werden können. In sicherheitskritischen Fällen sind ergänzende Methoden ohne Stichprobencharakter wie die Verifikation (z.B. durch Model-Checking) von Systemeigenschaften hinzuzuziehen.
Die wichtigsten Verfahren
Funktionale Tests werden sowohl nach der verfügbaren bzw. benutzten Information über das zu testende System als auch nach den unterschiedlichen Strategien, wie hinreichend viele repräsentative Testfälle gefunden werden, unterschieden. Gemeinsames Ziel ist immer, das komplette Verhalten eines Systems zu überprüfen, ohne alle möglichen Abläufe für alle erlaubten oder unerlaubten Eingaben zu testen, da dies schon bei einfachen Systemen mit vertretbarem Aufwand nicht möglich ist.
-
Strukturelle Tests, auch White- oder Glass-Box-Tests genannt, nutzen die Informationen aus dem Programmcode über den Kontroll- und Datenfluss [Muchnick, 1997], um für die Auswahl ihrer Testfälle sicherzustellen, dass – mit zunehmendem Aufwand – zumindest jede Anweisung, in der Regel jeder durch Bedingungen und Schleifen formulierte Programmzweig, oder sogar jede Kombination von Zweigen des Programms in mindestens einem Testfall ausgeführt und damit das komplette Verhalten des Programms durch Testfälle überdeckt wird. Typische Ergebnisse solcher Tests sind das Auffinden von Programmteilen, die nie erreicht werden oder die Benutzung von Variablen bevor sie initialisiert wurden. Voraussetzung für die Nutzung struktureller Tests ist das Vorliegen des Programmcodes, was in vielen Situationen, wie z.B. bei der Benutzung von Bibliotheken anderer Hersteller, nicht für ein gesamtes System angenommen werden kann.
-
Hier setzen sogenannte Black-Box-Tests an, die auf der Grundlage eines ausführbaren Systems testen ohne Quellcode-Informationen zu berücksichtigen. Die wichtigste Grundlage liefern die Schnittstellen des zu testenden Programms mit Ein- und Ausgabeparametern. Liegen zusätzlich vorher spezifizierte Beziehungen zwischen Ein- und Ausgabe vor, können neben Fehlverhalten wie Endlosschleifen oder Abstürzen auch Berechnungsfehler in scheinbar korrekt ablaufenden Fällen gefunden werden. Typische Vorgehensweisen beruhen auf der Partitionierung der Wertebereiche der Eingaben in Äquivalenz-Klassen mit jeweils gleichem Verhalten, so dass ein Repräsentant jeder Klasse ausreicht, um alle Fälle abzudecken. Für eine Arithmetik-Funktion wären dies z.B. alle Kombinationen aus negativen und positiven Parametern, für das Suchen in einer Liste jeweils der erfolgreiche und nicht erfolgreiche Fall für eine Liste mit einem oder mehreren Elementen. Hinzu kommt in der Regel die Abdeckung von Grenzfällen, die häufig in Entscheidungen benutzt werden oder Probleme bereiten, also z.B. die Null oder maximal darstellbare Werte bei Arithmetik, leere Listen oder nicht-definierte Objektreferenzen. Hinzu kommt häufig die Nutzung von angemessen verteilten Zufallswerten, um unvorhergesehene Kombinationen ebenfalls mit einer gewissen Wahrscheinlichkeit abzudecken.
In vielen Fällen unterstützen Werkzeuge heute beide Verfahrensweisen, die in der Praxis auch in Kombination eingesetzt werden, um möglichst viel Information zur Verbesserung der Testüberdeckung und zur Reduktion des Testaufwands zu nutzen.
Literatur
Muchnick, Steven S. : Advanced Compiler Design and Implementation. Morgan Kaufman : San Fransisco, 1997