Test

Ziel des Testens, ist es möglichst viele Fehler aufzudecken. Ein Fehler ist jede Abweichung des Verhaltens von dem in der Anforderungsdefinition festgelegten Verhalten. Testen ist eine projektbegleitende Maßnahme zur Qualitätssicherung und bezieht sich auf die Phasen Systemspezifikation, Entwurf, Implementierung und Wartung. Wenn dennoch von einer eigenen Testphase im Software-Life-Cycle die Rede ist, dann deshalb, weil der Test des Endprodukts erst nach Abschluss der vorhergegangenen Phasen möglich ist. Die im folgenden beschriebenen Tätigkeiten verteilen sich aber auf alle Phasen des Software-Life-Cycle, und finden nicht nur in der Testphase statt.
Eng verbunden mit dem Testen ist das Debugging, mit dem Fehlerursachen gefunden und behoben werden sollen, während Testen nur darauf abzielt, Fehler zu entdecken.

Testmethoden

Was wird mit welchem Ziel und mit welchen Methoden getestet?
Systemspezifikation
Vollständigkeit, Klarheit, Konsistenz und Realisierbarkeit ist im Spezifikationstest zu prüfen. Eine wichtige Rolle dabei spielen Prototypen der Benutzerschnittstelle und einzelner Systemkomponenten. Der Spezifikationstest muss stets mit dem Benutzer gemeinsam ausgeführt werden.
Module
Abweichungen zwischen der Modulimplementierung und der Modulspezifikation sind im Modultest aufzudecken. Module sind aber häufig nicht für sich alleine lauffähig; sie können oft nur ausgeführt werden, wenn andere Module vorliegen. Beim Modultest muss daher oft eine passenden Modulumgebung geschaffen werden, die das Verhalten der (noch) fehlenden Module simuliert. Diese Testumgebung bereitzustellen, ist oft ein wesentlicher Teil der Arbeit beim Modultest. Der Modultest erfolgt  während der Implementierungsphase und wird vom Modulimplementierer ausgeführt. Erst nach bestandenem Modultest wird eine Modul zur Verwendung im Programmsystem freigegeben.
Modulverbindungen
Nach erfolgreichen Modultests zielt der Integrationstest darauf ab, Module zu Subsystemen zusammenzufassen und dabei Fehler in der Kommunikation der Module untereinander aufzudecken. Auch für Subsysteme müssen Testumgebungen geschaffen werden.
Gesamtsystem
Sobald die Integration aller Subsysteme zum Gesamtsystem möglich ist, kann die Suche nach Abweichungen des Systemverhaltens von dem in der Anforderungsdefinition beschriebenen beginnen. Nicht nur Vollständigkeit der Benutzeranforderungen und Korrektheit der Ergebnisse sind zu prüfen, auch Zuverlässigkeit und Robustheit des Systems gegen fehlerhafte Benutzereingaben. Darüberhinaus sind die nicht-funktionalen Anforderungen wie z.B. Effizienz, ..  zu prüfen. Umfang und Anzahl der Tests sowie die Wahl der richtigen Testdaten sind vom jeweiligen Anwendungsfall abhängig.
Akzeptanz
Die Entwicklung eines SW-Produktes endet mit dem Abnahmetest durch den Benutzer. Das SW-Produkt wird jetzt mit realen Daten unter realen Einsatzbedingungen getestet. Erst dieser Test zeigt, ob die Anwendung sowohl mit der vereinbarten Anforderungsdefinition als auch mit den Erwartungen des Benutzers übereinstimmt.
Bei den oben aufgeführten Tests können die folgenden Testmethoden eingesetzt werden:

Verifikation von Algorithmen

Mit einem Test lassen sich Fehler entdecken. Tests können jedoch nicht die Abwesenheit von Fehlern zeigen: Ein bestandener Test garantiert nicht, dass die entworfene Komponente wirklich korrekt ist, d.h. dass sie keine Fehler enthält. Zu diesem Zweck sind verschiedene, mathematisch orientierte Verifikationstechniken vorgeschlagen worden. Sie zielen darauf ab, die Korrektheit einer Komponente in voller mathematischer Strenge zu beweisen.  Diese Techniken sind für den  Korrektheitsbeweis kurzer und einfacher Algorithmen erfolgreich angewendet worden. Bei grossen Programmsystemen steigen aber die Schwierigkeiten so stark an, so dass hier Verifikation keine praktische Testhilfe darstellt.

Statische Programmanalyse

Hierbei gilt es, durch syntaktische, strukturelle oder semantische Analyse Fehler frühzeitig zu finden, ohne das Testobjekt direkt auszuführen.  Die wichtigsten Tätigkeiten sind:
Code-Inspektion
Der Autor bespricht mit anderen Softwaretechnikern sein Programm Schritt für Schritt. Fagan schlägt dazu ein Team von vier Personen vor: einen erfahrenen, am Projekt nicht beteiligten SW-Techniker als Moderator, den Designer des Testobjektes, den für die Implementierung zuständigen Programmierer und den für den Test verantwortlichen Mitarbeiter. Entdeckte Fehler werden vom Moderator notiert und klassifiziert. Zum Ende der Inspektion werden Designer und Programmierer mit der Korrektur beauftragt. Während der Inspektion werden nur Fehler aufgedeckt, nicht deren Korrektur diskutiert.
Komplexitätsanalyse
Maßzahlen für die Komplexität eines Programmms werden ermittelt, meist von eigens dafür geschaffenen Werkzeugen. Zu diesen Masszahlen gehören z.B. Komplexitätsmaße von Modulen (nach McCabe oder Halstead), Schachtelungstiefen von Schleifen, Längen von Prozeduren, Import- und Verwendungszahlen von Modulen, ...Damit soll man schneller fehlerträchtige Stellen im Programm einkreisen können, nach der Faustregel, dass komplexe Programmteile eher Fehler enthalten als Teile mit geringerer Komplexitätsmaßzahl. Die Aussagekraft dieser Maßzahlen ist allerdings umstritten, Komplexität ist schwer objektiv zu messen.
Strukturanalyse
Strukturanomalien wie z.B. unerreichbarer Programmtext ("toter Code"), Schleifen mit Einsprüngen, unerlaubte Kontrollstrukturen ("goto")  werden (von Werkzeugen) aufgedeckt. Oft sind sie ein Indiz für tieferliegende Fehler.
Datenflussanalyse
Datenflussanomalien wie z.B. Verwendung eines Datenobjektes vor seiner Initialisierung oder Nichtverwendung eines Datenobjektes nach einer Zuweisung liefern oft auch Hinweise für tieferliegende Fehler.

Dynamisches Testen

Hierbei werden die Testobjekte ausgeführt oder simuliert. Die wichtigsten Tätigkeiten sind dabei:

Black-Box und White-Box-Test

Prinzipiell kann jedes Testobjekt auf zwei Arten geprüft werden:
 
als Black-Box
ohne Kenntnis der inneren Struktur (Schnittstellentest). Abweichungen des Testobjektes von seiner Schnittstellenbeschreibung sollen aufgedeckt werden. Nur die Information in der Schnittstellenbeschreibung kann zur Konstruktion von Testfällen verwendet werden. Mit diesem Ansatz ist es nicht möglich hrauszufinden, ob alle Funktionen des Testobjektes wirklich benötigt werden oder ob Datenobjekte manipuliert werden, die keinen Einfluss auf das Verhalten des Objektes haben.
als White-Box
mit Berücksichtigung der inneren Struktuer (Schnittstellen- und Strukturtest). Bei der Wahl der Testfälle wird u.a. berücksichtigt, dass
Alle Pfade zu testen ist selbst bei automatischer Testdatengenerierung praktisch unmöglich, weil dann zuviele Testläufe benötigt werden.

Testplanung und -ausführung

Die Testobjekte der unteren Schichten (Module bzw. Klassen) eines SW-Systems sollen von den Implementierern selbst, die Integration von Testobjekten zu Subsystemen  sollte jedoch von Mitarbeitern getestet werden, die nicht an der Implementierung beteiligt waren.

Testplanung

Möglichst früh, am besten schon in der Phase der Systemspezifikation sollen folgende Punkte zumindest grob festgelegt werden:

Testobjekte vorbereiten

Erkennt man einen Fehler, so ist er im nächsten Schritt  zu lokalisieren. Am einfachsten lokalisiert man erkannte Fehler, indem man den Programmablauf verfolgt (tracing, breakpoints setzen) oder indem Zustände des Testobjektes verfolgt (Zustandsgrößen ausgeben) oder überwacht (z.B. Schwellwertüberwachung, Nichteinhaltung benutzerdefinierter Bedingungen, assertions) werden. Gängige Programmierumgebungen (Debugger, Inspector, ..) erleichtern diese Aufgaben sehr, trotzdem wird man immer noch die Testobjekte vorbereiten müssen, z.B. durch ein- und auschaltbare Anweisungen.

Testfälle und Testdaten auswählen

Eine Testfallbeschreibung gibt an, welches Testobjekt und welche seiner Funktionen geprüft wird, mit welchen Eingabedaten (= Testdaten) welche Ergebnisse erwartet werden.
Vollständiges Testen ist in der Regel mit den gegebenen Ressourcen unmöglich. Die richtige Auswahl von Testfällen kann die Fehlerentdeckung effizient gestalten, erfordert aber viel Erfahrung und Intuition.
Beim Strukturtest von Programmbausteinen reicht es nicht aus, Testfälle zu konstruieren, die sicherstellen, dass jede Anweisung mindestens einmal ausgeführt wird, wie das folgende Beispiel zeigt: Testdaten x,y,z mit z>=x>=y oder y>x oder y>z decken nicht auf, dass das Maximum nicht berechnet wird, wenn x das Maximum ist.

Testfälle sollten so ausgewählt werden, dass

Testumgebung bereitstellen

Eine brauchbare Testumgebung erlaubt es

Tests auswerten

Die Testfallbeschreibung legt fest, ob die Ergebnisse eines Testlaufes mit den erwarteten Ergebnissen übereinstimmt. Am besten überlässt man die Testauswertung eigens dafür entwickelten Programmen in der Testumgebung.
Wird ein Fehler entdeckt, ist seine Ursache zu finden. Meist werden dabei Hilfen zur Programmverfolgung und Zustandsüberwachung eingesetzt (Debugger, ..).
Ist die Fehlerursache erkannt, gibt es oft mehrere Möglichkeiten, ihn zu beheben. Vor der Entscheidung ist zu überlegen, welche Folgewirkung die Korrektur auf andere Teile des Testobjektes hat. Dazu ist es oft notwendig, den abstrakteren Entwurf zu studieren. Besondere Sorgfalt ist bei der Fehlerkorrektur notwendig, allzu leicht werden neue Fehler eingeschleust. Nach jeder Fehlerkorrektur sollen daher nicht nur alle Testfälle, die zur Entdeckung des Fehlers führten, sondern auch alle anderen Testfälle, denen das Testobjekt bisher unterzogen wurde, wiederholt werden.

Typische Fehler

Einige der häufigsten Fehler sind:

Testdokumentation

Dazu gehört