Testen ist die Grundlage, um qualitativ hochwertige Software zu erstellen. Obwohl das Testen im Softwareentwicklungsprozess einen hohen Stellenwert einnehmen sollte, wird es dennoch oft vernachlässigt. Vermutlich ein Grund dafür, dass Testen mit hohem Arbeitsaufwand verbunden ist und hauptsächlich per Hand durchgeführt werden muss.
In dieser Arbeit wird eine systematische Methode zur automatischen Testfallgenerierung für den Black-Box-Test vorgestellt. Das für die Umsetzung der Methode implementierte Werkzeug verknüpft Unit-Tests mit Äquivalenzklassen- und Grenzwertanalyse. Durch den Gebrauch von Quelltext-Vorlagen ist das Programm völlig unabhängig vom eingesetzten Unit-Testing-Framework und der verwendeten Programmiersprache. Darüber hinaus können die Testfälle auch als textuelle Beschreibung viel abstrakter formuliert werden.
Zur Einführung in die Thematik des Testens wird zuerst der Begriff Qualität definiert. Darauf aufbauend werden Prozessmodelle zur Softwareentwicklung vorgestellt und gezeigt wie sie Einfluss auf die Softwarequalität nehmen. Ein Überblick über Testen im Allgemeinen und Testverfahren im Besonderen ermöglichen die Einordnung der vorgestellten Testmethoden in einem breiteren Kontext. Im Anschluss daran werden die im Werkzeug umgesetzten Testmethoden ausführlich beschrieben. Das Benutzerhandbuch und ausgewählte Problemstellungen der Implementierung ermöglichen einen tieferen Einblick in das Werkzeug selbst. Ein Ausblick auf mögliche Verbesserungen und Weiterentwicklungen runden diese Arbeit ab.
Inhaltsverzeichnis
1 Einleitung
2 Grundlagen
2.1 Qualität
2.1.1 Produktqualität
2.1.2 Prozessqualität
2.2 Prozessmodelle
2.3 Testen
2.4 Testverfahren
2.4.1 Statische Verfahren
2.4.2 Dynamische Verfahren
3 Im Werkzeug umgesetzte Testverfahren
3.1 Äquivalenzklassenanalyse
3.1.1 Bestimmen der Äquivalenzklassen
3.1.2 Definition der Testfälle
3.1.3 Beispiel
3.2 Category-Partition Methode
3.2.1 Unabhängige Partitionen
3.2.2 Abhängige Partitionen
3.2.3 Beispiel
3.3 Grenzwertanalyse
3.4 Unit-Tests
4 Benutzerhandbuch
4.1 Einführung in TestIt
4.1.1 Systemvoraussetzungen
4.1.2 Installation
4.2 Funktionsweise
4.2.1 Erzeugen der Testdaten
4.2.2 Erzeugen des Testablaufs
4.3 Benutzeroberfläche
4.3.1 Menü- und Symbolleiste
4.3.2 Statusleiste
4.3.3 Standardschaltflächen für Dialoge
4.3.4 Datei-Dialog
4.3.5 Klassenansicht
4.3.6 Mitteilungen-Fenster
4.3.7 Ausgabe-Fenster
4.3.8 Eigenschaften-Fenster
4.3.9 Auflistungs-Editor
4.3.10 Bedingungen-Editor
4.3.11 Quelltext-Editor
4.3.12 Navigator
4.3.13 Programm-Einstellungen
4.4 Verwenden von TestIt
4.4.1 Erstellen der Metainformationen
4.4.2 Quelltext-Vorlagen erstellen
4.4.3 Testfälle vervollständigen
4.4.4 Ausführen der Tests
4.4.5 Beispiele
4.4.6 Tipps und Tricks
4.5 Erweitern von TestIt
4.5.1 Erstellen von Plugins
4.6 Referenz
4.6.1 Fehlermeldungen
4.6.2 Quelltext-Marken
4.6.3 Tastenkürzel
5 Implementierung von TestIt
5.1 Model-View-Controller
5.2 Datenmodell
5.2.1 PropertyGrid
5.2.2 Kulturinformationen
5.2.3 Laufzeit-Typinformationen
5.2.4 Persistenz
5.2.5 Testspezifikation
5.3 Testfallerzeugung
5.4 Plattformunabhängigkeit
5.5 Projekte
5.5.1 AssemblyParser
5.5.2 ComponentModel
5.5.3 Controls
5.5.4 MetaInfo
5.5.5 SharpParser
5.5.6 TestIt
5.5.7 TestSpecParser
5.5.8 Util
5.6 Laufzeitstatistik
6 Ähnliche Projekte
6.1 Condition-Table-Methode
6.2 Cause-Effect-Graphing
6.3 Revealing Subdomains
6.4 Partition Analysis
7 Ausblick
7.1 Verbesserung der Bedienung
7.2 Weiterentwicklung
7.2.1 Testing by Contract
7.2.2 Object Constraint Language
8 Zusammenfassung
9 Literaturverzeichnis
10 Glossar
11 Index
Kurzfassung
Testen ist die Grundlage, um qualitativ hochwertige Software zu erstellen. Obwohl das Testen im Softwareentwicklungsprozess einen hohen Stellenwert einnehmen sollte, wird es dennoch oft vernachlässigt. Vermutlich ein Grund dafür, dass Testen mit hohem Arbeitsaufwand verbunden ist und hauptsächlich per Hand durchgeführt werden muss.
In dieser Arbeit wird eine systematische Methode zur automatischen Testfallgenerierung für den Black-Box-Test vorgestellt. Das für die Umsetzung der Methode implementierte Werkzeug verknüpft Unit-Tests mit Äquivalenzklassen- und Grenzwertanalyse. Durch den Gebrauch von Quelltext-Vorlagen ist das Programm völlig unabhängig vom eingesetzten Unit-Testing-Framework und der verwendeten Programmiersprache. Darüber hinaus können die Testfälle auch als textuelle Beschreibung viel abstrakter formuliert werden.
Zur Einführung in die Thematik des Testens wird zuerst der Begriff Qualität definiert. Darauf aufbauend werden Prozessmodelle zur Softwareentwicklung vorgestellt und gezeigt wie sie Einfluss auf die Softwarequalität nehmen. Ein Überblick über Testen im Allgemeinen und Testverfahren im Besonderen ermöglichen die Einordnung der vorgestellten Testmethoden in einem breiteren Kontext. Im Anschluss daran werden die im Werkzeug umgesetzten Testmethoden ausführlich beschrieben. Das Benutzerhandbuch und ausgewählte Problemstellungen der Implementierung ermöglichen einen tieferen Einblick in das Werkzeug selbst. Ein Ausblick auf mögliche Verbesserungen und Weiterentwicklungen runden diese Arbeit ab.
Abstract
Testing is the basis for developing high quality software. Although testing should be held dear, it is still neglected very often. This is presumably because of the amount of work involved and it has to be done manually.
In this thesis a systematic approach for generating black-box tests automatically will be introduced. The implemented tool for realizing the approach combines unit testing, equivalence partitioning and boundary analysis. By making use of source code templates the program is entirely not constrained by the appointed unit-testing framework and the used programming language. Furthermore the test cases may also be expressed as a textual description in a more abstract way.
For the introduction of the topic, the term quality will be defined firstly. Based on this, software development methodologies are presented and it is shown how they influence software quality. A survey of testing in general and testing approaches in particular allows the classification of the presented testing approaches within a wider context. Following that the implemented testing approaches are described in detail. The user manual and selected problems of implementation provide a deeper insight into the tool itself. Prospects on program improvements and further development complete this thesis.
Abbildungsverzeichnis
Abbildung 1: Veranschaulichung des Qualitätsbegriffs, Quelle: [6]
Abbildung 2: Wasserfallmodell, Quelle: [11]
Abbildung 3: Einteilung der Testverfahren
Abbildung 4: Black-Box-Test, Quelle: [15]
Abbildung 5: White-Box-Test, Quelle: [15]
Abbildung 6: Grey-Box-Test, Quelle: [15]
Abbildung 7: Aufteilung des Wertebereichs
Abbildung 8: Testspezifikation für das Programm Find, Quelle [4]
Abbildung 9: Grenzwertanalyse, Quelle [4]
Abbildung 10: Model-View-Controller Entwurfsmuster, Quelle [19]
Abbildung 11: Datenmodell für die Metainformationen
Abbildung 12: Hierarchische Anordnung der Kulturen, Quelle [21]
Abbildung 13: Ein- und Ausgabeschema für Coco/R, Quelle [22] und [41]
Abbildung 14: Indexstruktur für die Testfallerstellung
Abbildung 15: Erzeugte Metainformationen
Abbildung 16: Erzeugte Vorlage für die Testmethode
Tabellenverzeichnis
Tabelle 1: Formular zur Auflistung der Äquivalenzklassen, Quelle [13]
Tabelle 2: Äquivalenzklassen des Programms Find
Tabelle 3: Testfälle für das Programm Find
Tabelle 4: Erzeugte Testframes bei unabhängigen Partitionen
Tabelle 5: Erzeugte Testframes bei abhängigen Partitionen
Tabelle 6: Tabellarische Testspezifikation für das Programm Find, Quelle [4]
Tabelle 7: Testframes für das Programm Find, Quelle [4]
Tabelle 8: Weitere Eigenschaften in den Metainfo-Klassen
Tabelle 9: Eigenschaften der Klasse TestCase
Tabelle 10: Attribute für das PropertyGrid, Quelle [20]
Tabelle 11: Legende für die Quelltextstatistik
Tabelle 12: Quelltextstatistik C# Dateien
Tabelle 13: Quelltextstatistik AssemblyParser
Tabelle 14: Quelltextstatistik ComponentModel
Tabelle 15: Quelltextstatistik Controls
Tabelle 16: Quelltextstatistik MetaInfo
Tabelle 17: Quelltextstatistik SharpParser
Tabelle 18: Quelltextstatistik TestIt
Tabelle 19: Quelltextstatistik TestSpecParser
Tabelle 20: Quelltextstatistik Util
Tabelle 21: Laufzeitstatistik für Parser (in Millisekunden)
Tabelle 22: Laufzeitstatistik Testfälle erstellen (in Millisekunden)
Danksagung
Ich möchte allen danken, die mir während der Entstehung dieser Diplomarbeit hilfreich und geduldig zur Seite gestanden sind. An erster Stelle gilt der Dank meinem Betreuer
o.Univ.-Prof. Dipl.-Ing. Dr. Hanspeter Mössenböck. Ich bedanke mich für seine Bemühungen, seine Unterstützung und seine fachkundigen Ratschläge.
Ganz besonderer Dank gilt meinen Eltern und meinen Freunden, deren Hilfe und konstruktive Kritik zur Verbesserung der Qualität dieser Diplomarbeit beigetragen haben.
Aufgabenstellung
Beim Black-Box-Test von Software geht es darum, aus der Spezifikation eines Programms Testfälle abzuleiten, ohne den Quelltext des Programms zu kennen. In der Regel werden diese Testfälle ad-hoc (d.h. ohne Systematik) ermittelt, was zur Folge hat, dass zahlreiche Testfälle vergessen werden und dass man sich nach Änderungen im Programm die Testfälle wieder neu überlegen muss.
Eine systematische Methode der Testfallgenerierung ist die Äquivalenzklassenanalyse, bei der die Werte der Eingabeparameter eines Programms in Klassen eingeteilt werden, die prinzipiell das gleiche Verhalten des Programms hervorrufen. Man muss dann das Programm nur mit einem einzigen Wert pro Äquivalenzklasse testen anstatt mit allen Werten dieser Klasse, was die Anzahl der Testfälle erheblich reduziert.
Ziel dieser Diplomarbeit ist ein Werkzeug, das die systematische Ermittlung von Testfällen nach der Äquivalenzklassenanalyse unterstützt. Es verwaltet Testspezifikationen für jede zu testende Methode des Programms (z.B. für die Methode string1.Insert(pos, string2)). Die Testspezifikationen werden vom Benutzer wie folgt erstellt:
· Man gibt alle Faktoren an, von denen die Methode abhängt, also alle Eingangs- und Ausgangsparameter sowie die globalen Variablen, die Einfluss auf die Methode haben. Für die Methode Insert sind das zum Beispiel die Parameter string1, string2 und pos
· Für jeden Faktor ermittelt man die Äquivalenzklassen, d.h. jene Werte, die prinzipiell das gleiche Verhalten der Methode hervorrufen. Zum Beispiel fallen die Werte "abc" und "def" für string2 in die gleiche Äquivalenzklasse, hingegen die Werte "abc" und null nicht. Außerdem versucht man Werte zu finden, die eine gewisse Wahrscheinlichkeit aufweisen, einen Fehler in der Methode aufzudecken (z.B. einen leeren String, einen String der Länge 1, einen sehr langen String).
Das Werkzeug ermittelt dann die erforderlichen Testfälle durch Kombination jedes Faktorwerts mit allen Werten der anderen Faktoren.
Um die Anzahl der Testfälle zu reduzieren, kann man Faktorenwerte die zu Fehlerfällen führen oder nur mit bestimmten anderen Faktorenwerten kombiniert werden dürfen, speziell markieren. Diese Faktorenwerte werden dann nicht mit allen anderen Faktorenwerten kombiniert, was die Anzahl der Testfälle erheblich senkt.
Als Ergebnis liefert das Werkzeug für jede spezifizierte Methode eine Liste von Testfällen, die man dann systematisch z.B. mit einem Werkzeug wie JUnit ausprogrammieren kann.
Die Arbeit ist in Java oder C# zu implementieren und mit einer grafischen Benutzeroberfläche zu versehen.
1 Einleitung
4. Juni 1996 - der Jungfernflug der Ariane 5 endet mit der Sprengung der Rakete cirka
40 Sekunden nach dem Start. Die Fracht, vier Satelliten, ging ebenfalls verloren. Der finanzielle Schaden belief sich ungefähr auf eine halbe Milliarde Euro. Der Selbstzerstörung der Ariane war der komplette Verlust der Steuerkontrolle vorangegangen. Was war passiert?
Das Programm des Navigationsrechners enthielt die Konvertierung einer 64-Bit-Fließkommazahl in eine 16 bittige vorzeichenbehaftete Ganzzahl. Wenn die Fließkommazahl zu groß ist, schlägt die Konvertierung fehl. So geschehen während des Starts der Ariane. Die dadurch entstandene Ausnahmesituation wurde vom Rechner nicht behandelt. Diese führte anschließend zur Abschaltung des Programms, wie in der Spezifikation vorgesehen. Daraufhin übernahm der zweite Navigationsrechner die Steuerung. Da auf beiden Rechnern die selbe Software lief, trat wiederum derselbe Fehler auf, welcher ebenfalls zur Abschaltung des Reservesystems führte. Daraufhin wich die Rakete vom Kurs ab, zerbrach und wurde aus Sicherheitsgründen gesprengt. [1]
Der Überlauf während der Typkonvertierung lässt einen Implementierungsfehler der Software vermuten. Das war jedoch nicht der Fall. Im Gegenteil, der Fehler war in den Anforderungen verborgen. Die verwendete Software für den Navigationsrechner der Rakete enthielt einen Programmteil vom Vorgängermodell Ariane 4. Die Verhaltensweise dieser Software wurde weder mit den Bedingungen der Ariane-5 verglichen, noch gemeinsam mit den anderen Ariane-5-Komponenten getestet. [2]
Das IEEE (Institute of Electrical and Electronics Engineers) unterscheidet drei Begriffe für Fehler, je nachdem wann sie auf dem Weg von der Ursache zur Wirkung auftreten. [3]
- Jeder Fehler (fault) oder Mangel ist seit dem Zeitpunkt der Entwicklung in der Software vorhanden. (Wiederverwendung eines Programmteils der Ariane-4 Software.)
- Ein Fehler ist aufgrund des fehlerhaften Verhaltens (error) eines Entwicklers entstanden. (Annahme, dass sich der Programmteil der Ariane-4-Software wieder verwenden lässt.)
- Ein Softwarefehler kommt nur bei der Ausführung der Software als Fehlerwirkung (failure) zum Tragen und führt dann zu einer Abweichung des tatsächlichen Programmverhaltens vom gewünschten Programmverhalten. (Überlauf bei der Typkonvertierung.)
Der Begriff Defekt (defect) fasst alle in einem Softwaresystem auftretenden Fehlerarten zusammen. [4] (Anmerkung: Der Begriff Bug (Wanze) als Synonym für Defekt entstand bereits vor der Erfindung des Computers. Im September 1945 entfernte die Mathematikerin Grace Murray Hopper eine Motte aus den Schaltwerken des Rechner-Urahns Havard Mark II. [5])
Defekte verursachen Schäden. Es gibt viele Arten von Schäden, wie finanzielle Schäden (Zerstörung der Satelliten), wie die Beschädigung des Rufs einer Firma, oder Personenschäden, wenn beispielsweise ein lebenserhaltendes Gerät fehlerhaft arbeitet. Durch Testen sollen diese Defekte gefunden und anschließend durch Debugging (der Begriff stammt von Bug) behoben werden. Debugging bedeutet eliminieren von Bugs.
Testen ist mit Kosten verbunden. Testen dient dazu, um Defekte zu finden und somit den Wert bzw. die Qualität der Software zu erhöhen. Die Wertsteigerung soll dabei höher ausfallen, als die verursachten Kosten. Ein Ziel des Testens ist also, möglichst viele Fehler bei möglichst geringem Aufwand zu finden. Dies führt zur Frage: Wie entwirft man eine minimale, aber annähernd vollständige Menge relevanter Testfälle?
Beim Testfallentwurf wird häufig unsystematisch vorgegangen. Die Testfälle werden ad hoc entwickelt und dabei werden nur gültige Eingabedaten getestet. Auf ungültige Eingaben wird oft vergessen. Außerdem werden die Designüberlegungen, die zu den Testfällen geführt haben, weder dokumentiert noch archiviert, um sie später nachvollziehen zu können. Nach Änderungen im Programm muss man sich die Testfälle wieder neu überlegen. Dies führt zur nächsten Frage: Wie wird beim Testen vorgegangen?
Um Defekte überhaupt zu finden, muss davon ausgegangen werden, dass sie in jeder Software vorhanden sind. Damit ein Softwareentwickler eine entsprechende Geisteshaltung annimmt, müsste er hinnehmen, dass er gerade fehlerhafte Software erstellt hat. So gesehen ist ein Softwareentwickler nicht als Tester seiner eigenen Software geeignet. Eine weitere Frage lautet: Wer testet?
Ziel dieser Arbeit ist es, die wirtschaftlichen, psychologischen und technischen Aspekte des Testens näher zu behandeln, wobei der Schwerpunkt auf der technischen Seite liegt. Es soll eine Systematik entwickelt werden, um eine minimale, aber möglichst vollständige Menge relevanter Testfälle für den Black-Box-Test zu entwerfen und zu dokumentieren. Anschließend sollen aus den Testfällen automatisch Testprogramme erzeugt werden.
2 Grundlagen
Dieses Kapitel definiert zuerst den Begriff Qualität. Im Anschluss daran werden Produkt - und Prozessqualität behandelt, und es wird erklärt, warum sie einander bedingen. Weiters wird auf standardisierte Prozessmodelle und wie sie dabei helfen Prozessqualität sicherzustellen eingegangen. Anschließend folgt eine allgemeine Betrachtung des Begriffs Testen für die Umsetzung der Produktqualität. Den Abschluss machen eine Einteilung der Testverfahren, wie sie zur Erhöhung der Softwarequalität beitragen und mit welchen Metriken deren Erfolg gemessen wird.
2.1 Qualität
Qualität ist die Übereinstimmung von geforderter Beschaffenheit (Qualitätsforderung) und realisierter Beschaffenheit (siehe Abbildung 1). Das Niveau der Qualitätsforderung ist dabei nicht ausschlaggebend. Wird die Qualitätsforderung nicht erfüllt, liegt ein Qualitätsmangel vor. Übersteigt die realisierte Beschaffenheit die Qualitätsforderung, trägt dies nichts zur Erreichung von Qualitätszielen bei. Grundsätzlich wird zwischen Produktqualität und Prozessqualität unterschieden. [6]
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 1: Veranschaulichung des Qualitätsbegriffs, Quelle: [6]
2.1.1 Produktqualität
Die Produktqualität beschreibt Qualitätsmerkmale des Produkts. Nach der ISO-Norm 9126 sind dies: [6]
- Änderbarkeit: beschreibt Merkmale, welche die Durchführung von Anpassungen an geänderte Anforderungen ermöglichen.
- Benutzbarkeit: beschreibt Merkmale, welche die Einfachheit oder Schwierigkeit der Nutzung durch den Menschen betreffen.
- Effizienz: beschreibt Merkmale, welche einen ökonomischen Gebrauch von Ressourcen des unterliegenden Systems ermöglichen.
- Funktionalität: beschreibt Merkmale, welche die Verwendbarkeit bezüglich der vorhandenen Funktionen ausdrücken.
- Übertragbarkeit: beschreibt Merkmale, welche die Eignung für andere Hardware- bzw. Software-Konfigurationen ausdrücken.
- Zuverlässigkeit: beschreibt Merkmale, welche das Verhalten im Fehlerfall beschreiben.
2.1.2 Prozessqualität
Prozessqualität wird vorausgesetzt, um Produktqualität erreichen zu können. Geringe Prozessqualität kann daher nicht zu hoher Produktqualität führen. [6] Das SEI (Software Engineering Institute der Carnegie Mellon University) hat mit finanzieller Hilfe des US-Verteidigungsministerium das Capability Maturity Model (CMM) entwickelt. Das CMM klassifiziert mit den fünf Reifegraden die Qualität des Softwareentwicklungsprozesses (SE-Prozesses) in einem Software-Unternehmen.
- Reifegrad 1 (initial): Der SE-Prozess ist instabil und kaum organisiert. Zeitaufwand und Kosten für die Entwicklung werden nur grob geschätzt.
- Reifegrad 2 (repeatable): Der SE-Prozess ist geregelt und wiederholbar. Methoden wie Analyse und Design werden eingesetzt.
- Reifegrad 3 (defined): Der SE-Prozess wird standardisiert und in einzelne Phasen unterteilt. Das Einhalten des Prozesses wird von einer Prozessgruppe überwacht.
- Reifegrad 4 (managed): Die Suche nach Ursachen für Abweichungen vom Projektplan und die ständige Korrektur des SE-Prozesses stehen im Vordergrund.
- Reifegrad 5 (optimized): Der SE-Prozess wird permanent untersucht und optimiert.
Das CMM ist hierarchisch organisiert. Ein Aufstieg zu einem höheren Reifegrad ist durch die Verbesserung der SE-Prozesse möglich. [8] CMM wurde Ende 2003 durch das Capability Maturity Model Integration (CMMI) ersetzt, welches ein modulares und vor allem vereinheitlichtes Modell für die Integration weiterer Entwicklungs-Disziplinen, wie zum Beispiel Hardware-Entwicklung, bietet. [9]
In Europa werden eher die Normen der ISO-9000 Reihe favorisiert. ISO 9000-3 erklärt beispielsweise die Anwendung von Qualitätsmanagementsystemen auf die Softwareentwicklung. Diese Norm ist dem amerikanischen CMM ähnlich, bietet jedoch keine Qualitätsniveaus. [8]
Damit ein SE-Prozess bei Projektbeginn nicht jedes Mal von Grund auf neu entwickelt werden muss, existieren bereits eine Vielfalt von standardisierten Prozessmodellen. Jedes Prozessmodell hat dabei seine spezifischen Vor- und Nachteile.
2.2 Prozessmodelle
Ein Prozessmodell beschreibt den SE-Prozess vollständig. Es definiert Aktivitäten (Vorgehensmodell) und Rollen (Projektorganisation), die bei der Entwicklung zu durchlaufen sind, und die dabei erzeugten Ergebnisse. [10]
Das einfachste aller Prozessmodelle ist das Wasserfallmodell (siehe Abbildung 2). Es beschreibt den SE-Prozess als sequenziell zu durchlaufende Phasen. Die nächste Phase wird erst begonnen, nachdem die vorherige Phase abgeschlossen wurde. [10] Es erlaubt zwar die Rückkehr zur vorherigen Phase, falls diese unvollständige Ergebnisse geliefert hat. Gesamt betrachtet ist das Wasserfallmodell allerdings wenig flexibel. Wenn sich während der Projektabwicklung die Anforderungen an die Software ändern, muss ein komplett neuer Zyklus begonnen werden. [11]
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 2: Wasserfallmodell, Quelle: [11]
Neuere Prozessmodelle legen besonderen Wert auf Flexibilität. Sie werden deshalb auch
agile Prozesse genannt. Im Gegensatz dazu werden die wenig flexiblen SE-Prozesse als schwergewichtig bezeichnet. Agile Prozesse ermöglichen diese Flexibilität durch kurze Versionszyklen von wenigen Wochen bis Monaten. Der bekannteste Vertreter dieser Kategorie von SE-Prozessen ist wohl Extreme Programming (XP). Die typischen Phasen der klassischen Softwareentwicklung (Anforderungen, Analyse, Entwurf, Implementierung, Test) werden in kurzen Zyklen, so genannten Iterationen, durchlaufen. Der Auftraggeber hat daher viel öfter die Möglichkeit, steuernd in das Softwareprojekt einzugreifen. [12]
Allen SE-Prozessen, schwergewichtigen wie agilen, ist gemeinsam, dass sich eine Phase bzw. mehrere Aktivitäten mit dem Testen der Software befassen. Der nächste Abschnitt beschäftigt sich daher mit Grundlagen des Testens.
2.3 Testen
Mit dem Testen von Software wird das Ziel verfolgt, sämtliche Fehler unterschiedlichster Art (siehe Einleitung) aufzuspüren. Das heißt, zu
1. prüfen, ob die Software das tut, was sie tun soll, und zu
2. prüfen, ob sie nicht das tut, was sie nicht tun soll. [13]
Der erste Fall wird Positivtest genannt. Er dient dazu, um zu zeigen, dass die Software ihren Zweck erfüllt. Die zweite Forderung soll die Software zu Fall bringen. Es ist also ein destruktiver Prozess, der auch als Negativtest bezeichnet wird. Ein erfolgreicher Test muss beide Forderungen erfüllen. [14]
Es gilt nun zu beurteilen, wie gut die Tests geeignet sind, Fehler zu finden. Wünschenswert wäre ein Messwert, der eine Aussage über die Qualität der Testfälle zulässt. So ein Messwert für die Ausprägung einer Eigenschaft wird als Metrik bezeichnet. Eine Metrik ist eine Maßzahl für eine Eigenschaft oder ein Qualitätsmerkmal von Software. Beispiele für Quelltext-Metriken sind: Anzahl der Quelltextzeilen, Komplexitätsmaßzahlen und Lesbarkeit. [9] Mehr dazu im Abschnitt 2.4.
Es stellt sich nun die Frage: Wer testet? Entwurf und Implementierung von Software ist ein konstruktiver Prozess. Psychologisch gesehen wäre es falsch einen Softwareentwickler nach erledigter Arbeit zu zwingen, seine eigene Software zu Fall zu bringen. Für eine destruktive Tätigkeit sind daher Außenstehende besser geeignet. Weiters könnte der Softwareentwickler ungenügendes Verständnis für die Problemstellung gehabt haben. In diesem Fall setzt sich das Missverständnis in den Tests fort und die Fehler bleiben unentdeckt. [14]
2.4 Testverfahren
Ein Testverfahren ist eine systematische Methode, um Tests zu erstellen und auszuwählen. Ein Verfahren ist effektiv, wenn es geeignet ist, Fehler zu finden. Jedes Verfahren adressiert dabei charakteristische Fehlerarten [14]. Abbildung 3 zeigt eine Einteilung der Verfahren, welche nachfolgend erklärt werden. Die fett umrahmten Testverfahren sind Teil der Aufgabenstellung. Sie werden im Kapitel 3 ausführlich erklärt.
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 3: Einteilung der Testverfahren
2.4.1 Statische Verfahren
Statische Verfahren sind dadurch gekennzeichnet, dass das Programm nicht ausgeführt wird. Stattdessen wird der Quelltext auf bestimmte Merkmale analysiert.
2.4.1.1 Schreibtischtest
Der Tester spielt einzelne Testfälle auf einem Blatt Papier durch. Er liest dabei Schritt für Schritt jede Anweisung im Quelltext und notiert sich das jeweilige Ergebnis. Anschließend vergleicht er das erwartete Ergebnis mit dem tatsächlichen. Dieses Verfahren ist aufwendig, ineffizient und für den Tester langweilig durchzuführen. [4]
2.4.1.2 Quelltext-Inspektion
Inspektionen erfordern in der Regel einige Vorbereitung. Schließlich trifft sich ein Testteam zum Gedankenaustausch und zur visuellen Überprüfung des Programms. Das Testteam besteht gewöhnlich aus vier Mitgliedern, von denen einer der Autor des Programms ist. Außerdem ist ein Moderator vorgesehen, der nicht der Programmautor sein sollte. Der Moderator verteilt einige Tage vor der Inspektionssitzung Unterlagen wie Spezifikation und Quelltext, damit die Teilnehmer genügend Zeit haben, sich mit der Materie auseinanderzusetzen. In der Sitzung erklärt der Autor des Programms schrittweise die Programmlogik. Während des Vortrags können die Teammitglieder Fragen stellen, um mögliche Fehlerquellen aufzudecken. Erfahrungsgemäß findet der Softwareentwickler während des Vortrags die Fehler selbst. Der Moderator steuert die Diskussion und achtet darauf, dass sich das Team auf die Fehlersuche und nicht auf deren Korrektur konzentriert. Prüflisten helfen dabei, häufig auftretende Fehler zu finden. Sie sollten ständig überarbeitet und ergänzt werden. [13] Erkannte Schwächen im Quelltext sind beispielsweise nicht eingehaltene Programmierrichtlinien und Rückmeldungen über den Programmierstil.
2.4.1.3 Komplexitätsanalyse
Die Komplexitätsanalyse beschäftigt sich, wie der Name schon sagt, mit der Komplexität des Quelltexts. Die meisten Komplexitätsmaße lassen sich vom Steuerfluss des Programms ableiten. Das wohl bekannteste Maß ist die zyklomatische Komplexität nach McCabe. Bei diesem Verfahren wird der Steuerfluss als Graph dargestellt. Die Komplexitätsmaßzahl hängt von der Anzahl der Knoten, Anzahl der Kanten zwischen den Knoten und der Anzahl der nicht verbundenen Teilgraphen ab. [14] Andere Maße berücksichtigen die Anzahl der Datentypen, Verzweigungen und die Verschachtelungstiefe. Weiters versuchen Werkzeuge zur Quelltextanalyse schwächen im Quelltext bzw. unsauberen Programmierstil und damit potentielle Fehlerquellen aufzudecken.
2.4.2 Dynamische Verfahren
Im Gegensatz zu den statischen Testverfahren wird bei den dynamischen Verfahren das Programm ausgeführt. In [13] wird Testen folgendermaßen definiert: Testen ist der Prozess, ein Programm mit der Absicht auszuführen, Fehler zu finden. Die bedeutendste Metrik für dynamische Verfahren ist die Testabdeckung. Als Testabdeckung bezeichnet man das Verhältnis von tatsächlich getroffenen Aussagen eines Tests zu den theoretisch möglichen Aussagen. Die Testabdeckung kann durch verschiedene Faktoren beeinflusst werden. Durch eine höhere Anzahl an Testfällen lässt sich die Abdeckung verbessern. In der Praxis wird die Testabdeckung jedoch durch den Testaufwand und den damit verbundenen Kosten begrenzt. [9]
2.4.2.1 Black-Box-Test
Das wesentliche Merkmal von Black-Box-Tests ist, dass die interne Struktur eines Programms entweder nicht bekannt ist, oder ignoriert wird. Das Programmverhalten wird anhand der Spezifikation getestet. Das bedeutet, dass die Testdaten aus der Spezifikation abgeleitet werden. Abbildung 4 verdeutlicht dies grafisch.
Wichtige Metriken für die Testabdeckung von Black-Box-Tests sind:
- Abdeckung aller Funktionen: jede spezifizierte Funktion wird in mindestens einem Testfall ausgeführt.
- Abdeckung aller Eingaben: für jede Funktion wird jeder Eingabewert mindestens in einem Testfall verwendet.
- Abdeckung aller Ausgaben: für jede Funktion wird jeder Ausgabewert in mindestens einem Testfall erzeugt.
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 4: Black-Box-Test, Quelle: [15]
Um mit diesem Verfahren alle Fehler in einem Programm zu finden, müssten alle möglichen Eingabewerte zum Testen verwendet werden. Dies ist jedoch unmöglich, denn selbst bei einem ganzzahligen Wert gäbe es theoretisch unendlich viele Testwerte. Daraus ergeben sich zwei Folgerungen:
1. Man kann im Allgemeinen ein Programm nicht mit allen möglichen Eingaben testen, da es unendlich viele davon geben könnte. Somit kann man durch Tests nicht zeigen, dass alle eingaben das korrekte Ergebnis liefern.
2. Ein wesentlicher Aspekt beim Testen ist die Wirtschaftlichkeit.
Charakteristisches Merkmal der Black-Box-Testverfahren ist es, die Anzahl der Testfälle so einzuschränken, dass mit den verbleibenden die maximale Anzahl an Fehlern gefunden wird. Die beiden Verfahren Äquivalenzklassenanalyse und Grenzwertanalyse verfolgen genau dieses Ziel und werden im Kapitel 3 erklärt. [13]
2.4.2.2 White-Box-Test
White-Box-Tests berücksichtigen die interne Struktur eines Programms. Der Tester formuliert die Testfälle mit Kenntnis des Quelltexts selbst oder des darauf basierenden Algorithmus (siehe Abbildung 5). [13]
Wichtige Metriken für die Testabdeckung von White-Box-Tests sind:
- Abdeckung aller Anweisungen: Führt jede Anweisung mindestens einmal aus.
- Abdeckung aller Verzweigungen: Jede Verzweigungsalternative wird mindestens einmal ausgeführt. Eine If-Else -Verzweigung weist zwei mögliche Pfade auf.
- Abdeckung aller Bedingungen bei Verzweigungen: Jede Verzweigungsbedingung muss mindestens einmal wahr und einmal falsch gewesen sein. Bei zwei Bedingungen in einer If-Else -Verzweigung ergeben sich 2² = 4 Möglichkeiten. Beispiel: if (a&b)
Abbildung in dieser Leseprobe nicht enthalten
- Abdeckung aller Pfade: Jeder mögliche Kontrollflusspfad durch das Programm muss mindestens einmal durchlaufen werden. Im Allgemeinen ist dies unmöglich, wenn beispielsweise die Anzahl an Schleifendurchläufen durch eine unbekannte Größe bestimmt wird.
Wie die angeführten Beispiele belegen, nimmt die Anzahl an Möglichkeiten von der Anweisungsabdeckung bis zur Pfadabdeckung kontinuierlich zu. Werkzeuge messen meist nur Abdeckung auf Anweisungs- bzw. Verzweigungsebene. [4]
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 5: White-Box-Test, Quelle: [15]
2.4.2.3 Grey-Box-Test
Grey-Box-Tests verbinden die Vorteile der White-Box-Tests mit denen der blackboxorientierten Verfahren. [13] White-Box daher, weil der Softwareentwickler auch die Tests schreibt und eventuell doch einige Implementierungsdetails, wie den der Software zugrunde liegenden Algorithmus, kennt. Blackboxorientiert, weil die Tests vor der Implementierung der Software geschrieben werden und daher der Quelltext noch nicht bekannt ist. [9] Abbildung 6 stellt den Zusammenhang grafisch dar.
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 6: Grey-Box-Test, Quelle: [15]
3 Im Werkzeug umgesetzte Testverfahren
In diesem Kapitel werden die für die Aufgabenstellung relevanten Verfahren zur systematischen und automatisierten Testfallerzeugung vorgestellt. Der Benutzer des Werkzeugs erstellt aus der Spezifikation der zu testenden Funktion eine maschinenlesbare Testspezifikation mittels Äquivalenzklassenanalyse. Das Werkzeug liest die Testspezifikation ein und erstellt daraus Testframes. Konkrete Testwerte werden mittels Grenzwertanalyse ermittelt. Anschließend werden die Testframes und Testwerte zu Unit-Tests zusammengefügt.
3.1 Äquivalenzklassenanalyse
Wie im Abschnitt 2.4.2.1 erwähnt, existieren drei Abdeckungsmaße für Black-Box-Tests. Die Funktionsabdeckung ist einfach zu erreichen, indem beispielsweise jede Methode einer Klasse getestet wird. Schwieriger, wenn nicht unmöglich, ist es, die Abdeckung aller Eingaben bzw. Ausgaben zu bewerkstelligen, weil die Anzahl der möglichen Testfälle ins Unendliche gehen kann. Es ist daher unausweichlich, aus der Menge an möglichen Testfällen, jene herauszufinden welche
1. eine gewisse Wahrscheinlichkeit zum Auffinden von Fehlern aufweisen.
2. die Anzahl anderer notwendiger Testfälle reduzieren.
3. eine große Menge andere Testfälle überdecken.
Punkt zwei sagt aus, dass jeder Testfall so viele unterschiedliche Eingabebedingungen wie möglich in Betracht ziehen sollte. Der dritte Punkt impliziert, dass der Wertebereich für Eingaben in eine endliche Anzahl an Untermengen, auch Äquivalenzklassen oder Partitionen genannt, unterteilt wird. Abbildung 7 zeigt die Aufteilung des gesamten Wertebereichs in einzelne Partitionen grafisch.
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 7: Aufteilung des Wertebereichs
Diese Aufteilung geschieht unter der Annahme, dass ein repräsentativer Wert einer bestimmten Äquivalenzklasse genauso gut geeignet ist, Fehler zu finden bzw. auszuschließen, wie jeder andere Wert auch. Das heißt, wird durch einen Repräsentanten aus einer Klasse ein Fehler gefunden, so wird dies auch von jedem anderen Wert in der Klasse erwartet. Das Aufstellen von Testfällen erfolgt in zwei Schritten:
1. Bestimmen der Äquivalenzklassen.
2. Definition der Testfälle.
3.1.1 Bestimmen der Äquivalenzklassen
Zum Festlegen der Äquivalenzklassen verwendet man eine Eingabebedingung oder externe Bedingung, welche üblicherweise in der Spezifikation durch einem Satz oder Abschnitt repräsentiert wird, und teilt diese in Gruppen auf. Es sind mindestens zwei Gruppen, nämlich gültige und ungültige Partitionen. Diese Gruppen können bei Bedarf weiter unterteilt werden. Tabelle 1 ist bei der Auflistung der Äquivalenzklassen behilflich.
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 1: Formular zur Auflistung der Äquivalenzklassen, Quelle [13]
Äquivalenzklassen beziehen sich auf eine Eingabebedingung oder externe Bedingung aus der Spezifikation. Daher wird die Art und Menge der Partitionen von diesen Bedingungen bestimmt. Folgende Einteilung hilft beim Auffinden der Äquivalenzklassen:
- Wertebereich: Beispielsweise kann eine Variable einen Wert zwischen 1 und 999 annehmen. Daraus ergeben sich eine Partition mit gültigen Werten (1 <= Wert <= 999), sowie zwei Partitionen mit ungültigen Werten (Wert < 1 und 999 < Wert).
- Anzahl von Werten: Bei einer Anzahl von Werten (zum Beispiel: 1 bis 6 Eigentümer) wird ähnlich verfahren. Folglich werden eine Partition mit gültigen Werten (1 bis 6 Eigentümer) sowie zwei Partitionen mit ungültigen Werten (kein Eigentümer, mehr als 6 Eigentümer) abgeleitet.
- Menge von Werten: Bei einer Menge von Werten (zum Beispiel: ein KFZ kann PKW, LKW oder Motorrad sein) wird für jede Alternative eine Äquivalenzklasse erstellt. Zusätzlich zu den gültigen Partitionen wird eine ungültige Klasse mit einer nicht genannten Möglichkeit hinzugefügt.
- Muss-Bedingung: Für eine Muss-Bedingung wird je eine Partition, welche die Bedingung erfüllt bzw. nicht erfüllt, erstellt. Für die Bedingung „das erste Zeichen muss ein Buchstabe sein“ wird eine Klasse für „erstes Zeichen ist ein Buchstabe“ und eine weitere für „erstes Zeichen ist kein Buchstabe. erzeugt.
3.1.2 Definition der Testfälle
Der zweite Schritt umfasst die Definition der Testfälle:
- Jeder Äquivalenzklasse wird eine eindeutige Nummer zugewiesen.
- Aus den bisher nicht behandelten gültigen Klassen wird ein neuer Testfall erstellt, der möglichst viele weitere unbehandelte Klassen abdeckt. Dies wird so lange wiederholt, bis alle Äquivalenzklassen durch Testfälle abgedeckt sind. Die Auswahl der Testwerte kann beispielsweise durch Grenzwertanalyse erfolgen (siehe Abschnitt 3.3).
- Aus jeder ungültigen Äquivalenzklasse wird jeweils ein gesonderter Testfall erstellt.
Ungültigen Klassen kommt durch die Einzeltests besonders Augenmerk zu. Das geschieht aus dem Grund, weil fehlerhafte Eingaben nicht gemeinsam mit weitern fehlerhaften Eingaben getestet werden sollen. Es könnte sonst passieren, dass eine fehlerhafte Eingabe durch eine andere verhüllt wird. Falls zum Beispiel die Spezifikation den Satz „Geben Sie den Buchtyp (gebunden, Taschenbuch, lose) und die Anzahl (1-9999) ein“ enthält, würde beim Testfall Buchtyp=xyz und Anzahl=0 beispielsweise nach der fehlgeschlagenen Prüfung des Buchtyps das Programm abgebrochen. Die fehlerhafte Anzahl würde daher nicht mehr geprüft. [13]
3.1.3 Beispiel
Das Programm Find sucht ein gegebenes Muster in einer Datei. Alle Zeilen, die das Muster enthalten, werden am Bildschirm ausgegeben. Wenn eine Zeile das Muster mehrfach enthält, wird es trotzdem nur einmal ausgegeben. Syntax: Find <Muster> <Datei>
Das Muster ist eine beliebige Zeichenkette, die nicht länger als die maximale Zeilenlänge der Sätze in der Datei sein darf. Wenn das Muster Leerzeichen enthält, muss es unter Hochkomma (" ") geschrieben werden. Ein Hochkomma im Muster selbst wird durch Voranstellen eines Schrägstriches (\") angegeben. [16]
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 2: Äquivalenzklassen des Programms Find
Durch die Analyse der Spezifikation lassen sich externe Bedingungen und Äquivalenzklassen, wie in Tabelle 2 gezeigt, identifizieren. Jede Äquivalenzklasse hat eine eindeutige Nummer, die in Klammern neben der Äquivalenzklasse angeführt ist.
Im nächsten Schritt werden Testfälle erstellt, die eine oder mehrere Äquivalenzklassen abdecken. Jede ungültige Äquivalenzklasse wird gesondert getestet. Tabelle 3 zeigt die erstellten Testfälle für gültige sowie ungültige Eingaben. Neben jedem Testfall sind die abgedeckten Äquivalenzklassen aufgelistet.
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 3: Testfälle für das Programm Find
Zu den Testfällen in Tabelle 3 ist noch Anzumerken, dass eine Datei namens file_not_found nicht existieren darf bzw. die Datei valid_file eine gültige Datei darstellt.
3.2 Category-Partition Methode
Eine systematischere Vorgehensweise bei der Erstellung von Äquivalenzklassen verfolgt die Category-Partition Methode. [16] Dabei wird die in einer natürlichen Sprache verfasste Spezifikation, welche unmittelbar als Basis für Black-Box-Tests ungeeignet ist, in eine formale Sprache übersetzt. Während der Transformation der Spezifikation fallen häufig unvollständige, mehrdeutige oder widersprüchliche Formulierungen auf. Dieser Übersetzungsprozess hilft daher auch die Qualität der Spezifikation zu verbessern, indem die vorher genannten Probleme aufgedeckt werden. Weiters werden nicht nur die Eingabedaten, sondern auch die Ausgabedaten bei den Tests berücksichtigt.
Die Category-Partition Methode beginnt mit der Zerlegung der Spezifikation in funktionale Einheiten, die unabhängig getestet werden können. Im Fall einer Klasse (im Sinne der objektorientierten Programmierung) wäre eine solche funktionale Einheit eine Methode. Der nächste Zerlegungsschritt ermittelt die Parameter und Umgebungseinflüsse, welche das Verhalten der funktionalen Einheit beeinflussen. Parameter sind explizite Eingaben, während Umgebungseinflüsse beispielsweise globale Variablen oder den Systemzustand beschreiben. Parameter und eventuell vorhandene Umgebungseinflüsse werden unter dem Begriff Faktor zusammengefasst. Für diese Faktoren müssen konkrete Werte gefunden werden, um Testfälle erstellen zu können. Auf der Suche nach diesen Werten werden im nächsten Schritt des Zerlegungsprozesses Arten von Informationen gesucht, die jeden Faktor kennzeichnen. Der Tester markiert in der Spezifikation für jeden Faktor jene Textstellen, die das Verhalten der funktionalen Einheit beeinflussen. Jedes erkannte Merkmal wird als Category bezeichnet. Im nächsten Schritt werden die gefundenen Merkmale in Äquivalenzklassen (Choices) unterteilt. Die Begriffe Äquivalenzklasse, Partition sowie Choice haben dieselbe Bedeutung.
Nachdem Faktoren und Äquivalenzklassen feststehen, wird für jede funktionale Einheit eine formale Testspezifikation verfasst. Die Testspezifikation besteht aus einer Liste von Faktoren, die eine Liste von Partitionen enthält. Aus der Testspezifikation werden im nächsten Schritt Testframes für die Testfälle erstellt. Vorerst wird bei der Erstellung der Testframes so vorgegangen, dass jede Äquivalenzklasse mit jeder anderen Partition, die nicht zum selben Faktor gehört, kombiniert wird. Im Abschnitt 3.1.2 wird diese Vorgehensweise noch erweitert.
3.2.1 Unabhängige Partitionen
Den einfachsten Fall stellen unabhängige Partitionen dar. Aus einer Testspezifikation mit zwei Faktoren (Faktor 1 und Faktor 2), die jeweils zwei Äquivalenzklassen (Partition1 und Partition 2) enthalten, kann man vier Testframes ableiten. Tabelle 4 zeigt die erstellten Testframes. Beispielsweise wird Partition 1 von Faktor 1 wegen der besseren Übersichtlichkeit als F1_P1 bezeichnet.
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 4: Erzeugte Testframes bei unabhängigen Partitionen
Im Testframe ist jedem Faktor genau eine Partition zugeordnet. Die Anzahl der erzeugten Testframes erhält man, indem man die Anzahl an Partitionen jedes Faktors miteinander multipliziert.
3.2.2 Abhängige Partitionen
Im Allgemeinen sind Faktoren bzw. deren Partitionen voneinander abhängig. Ein einfaches Beispiel dafür sind die einzelnen Elemente des Datums. Es gibt Monate mit 30 Tagen, während andere Monate 31 Tage aufweisen. Die Anzahl der Tage ist offensichtlich vom Monat abhängig; nicht nur vom Monat, sondern auch vom Jahr. In Schaltjahren hat der Februar 29 Tage, andernfalls 28 Tage. Im Falle des 29. Februars ist der Tag also von Monat und Jahr abhängig. Für das Erstellen des Testframes ergibt sich daher die Notwendigkeit, dass einige Äquivalenzklassen nicht mit bestimmten anderen Partitionen kombiniert werden dürfen. Diese Einschränkungen (Constraints) müssen ebenfalls in die Testspezifikation aufgenommen werden.
Zur Demonstration soll das vorherige Beispiel mit unabhängigen Partitionen um eine Einschränkung zwischen F1_P1 und F2_P2 erweitert werden. F2_P2 soll von F1_P1 abhängig sein. F1_P1 wird daher als Bezugspartition und F2_P2 als abhängige Partition bezeichnet. Beziehungen zwischen Äquivalenzklassen werden in der Testspezifikation durch das zusätzliche Attribut Property in der Bezugspartition und einer If -Bedingung in der abhängigen Partition realisiert. Die If -Bedingung enthält den Bezeichner der Bezugspartition.
F1_P1 [property Dependent]
F2_P2 [if Dependent]
Verknüpfungen von mehreren Äquivalenzklassen, wie es im Datumsbeispiel notwendig wäre, sind mithilfe des binären Und -Operators möglich. Oder -Verknüpfungen sind realisierbar, indem bei mehreren Bezugspartitionen dasselbe Property -Attribut gesetzt wird.
Wegen der soeben definierten Beziehung zwischen F1_P1 und F2_P2 werden nur drei Testframes erstellt. Das in Tabelle 5 grau hinterlegte Testframe fällt durch die Einschränkung, dass F2_P2 nur mit F1_P1 kombiniert werden darf, weg.
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 5: Erzeugte Testframes bei abhängigen Partitionen
Die im Abschnitt 2.3 gestellte Forderung, auch ungültige Eingaben zu testen, wird durch die Testspezifikation ebenfalls berücksichtigt. Hierfür wird eine Partition, die fehlerhafte Eingaben enthält, mit dem Attribut Error versehen.
Als weiteres Attribut steht dem Tester Single zur Verfügung. Mit der Anzahl an Partitionen steigt die Anzahl der daraus erzeugten Testframes exponentiell an. Eine Variante der Testfallreduktion besteht darin, eine Partition nicht mit allen möglichen anderen zu kombinieren. In manchen Fällen reicht es aus, dass eine bestimmte Partition nur durch einen Testfall abgedeckt ist. Genau diesen Sachverhalt drückt das Single -Attribut aus. Jene Äquivalenzklasse, die mit diesem Attribut versehen ist, kommt in nur einem Testfall vor. Das Single -Attribut wird ebenfalls für spezielle oder ungewöhnliche Eingaben eingesetzt. So stellt beispielsweise der Zahlenwert Null für manche Berechnungen einen Spezialfall dar, bei dem die Verwendung des Single -Attributs zweckmäßig ist.
Äquivalenzklassen, die mit den Attributen Error bzw. Single versehen sind, werden als Spezialfälle bezeichnet. Der Begriff kommt daher, weil diese beim Erstellen der Testframes eine besondere Vorgehensweise erfordern. Spezialfälle dürfen nicht mit beliebigen anderen Partitionen kombiniert werden, sondern nur mit Nicht -Spezialfällen. Diese Forderung kommt daher, weil einerseits fehlerhafte Eingaben und andererseits Spezialfälle gesondert getestet werden sollen.
Zusammenfassend sind folgende Schritte zur Erstellung einer Testspezifikation erforderlich:
1. Analysieren der Spezifikation.
- Funktionale Einheiten ermitteln.
- Faktoren zu jeder funktionalen Einheit ermitteln.
- Eigenschaften jedes Faktors identifizieren.
2. Den Wertbereich der Faktoren in Partitionen unterteilen.
3. Beziehungen zwischen den Partitionen und Spezialfälle bestimmen.
3.2.3 Beispiel
Als Spezifikation soll wieder jene des Programms Find herangezogen werden. Tabelle 6 zeigt die aus der Spezifikation des Programms abgeleitete Testspezifikation. Spezialfälle sind kursiv, fehlerhafte Eingaben fett hervorgehoben. Die Beziehungen basieren auf folgenden impliziten Annahmen:
- Muster mit der Länge Null sowie Leerzeichen bzw. Hochkommas im Muster sind nur möglich wenn das Muster in Hochkommas steht.
- Eine Ausgabe ist nur möglich, wenn sowohl das Muster als auch die zu durchsuchende Datei gültig sind.
- Die Ausgabe der Vorkommen pro Zeile ist nur möglich, wenn das Musters mindestens einmal in der Datei vorkommt.
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 6: Tabellarische Testspezifikation für das Programm Find, Quelle [4]
Für die automatisierte Erstellung der Testframes mittels eines Werkzeugs muss die Testspezifikation in eine maschinenlesbare Form gebracht werden. Das heißt, dass beispielsweise statt der farblichen Hervorhebung der Spezialfälle die vorhin vorgestellten Attribute Error bzw. Single zum Einsatz kommen.
Länge des Musters:
0 [single] [if hochkomma]
1 [property muster]
1 < Länge < max. [property muster]
> max. Länge [error]
Muster in Hochkomma:
ja [property hochkomma]
nein [if muster]
falsche Hochkomma [error]
Leerzeichen im Muster:
ja [single] [if hochkomma]
nein
Hochkommas im Muster:
ja [single] [if hochkomma]
nein
Dateiname:
gültig [property datei]
nicht gefunden [error]
nicht angegeben [error]
Anzahl der Zeilen mit Vorkommen:
0 [single]
1 [if muster & datei] [property gefunden]
> 1 [single] [if muster & datei] [property gefunden]
Anzahl der Vorkommen pro Zeile:
1 [if gefunden]
> 1 [single] [if gefunden]
Abbildung 8: Testspezifikation für das Programm Find, Quelle [4]
Die Anzahl der Testfälle erhält man, indem man die Anzahl an Partitionen pro Faktor ohne Spezialfälle miteinander multipliziert. Anschließend wird die Gesamtanzahl an Spezialfällen hinzuaddiert. Weitere Einschränkungen durch Beziehungen zwischen den Äquivalenzklassen sind in der Berechnung nicht berücksichtigt. Die berechnete Anzahl an Testfällen stellt also die Maximalanzahl dar. Falls das Werkzeug zu viele Testframes erstellt hat, können diese durch Einführen von Beziehungen bzw. Spezialfällen in der Testspezifikation auf ein annehmbares Maß reduziert werden. Für das angeführte Beispiel sind das 4+6=10 Spezialfälle und 2×2×1×1×1×1×1 = 4 weitere Testframes. Tabelle 7 zeigt die durch das Werkzeug erstellten Testframes.
Abbildung in dieser Leseprobe nicht enthalten
Tabelle 7: Testframes für das Programm Find, Quelle [4]
Aus den Testframes werden im letzten Schritt konkrete Testfälle formuliert. Dabei wird aus jeder Äquivalenzklasse ein repräsentativer Wert ausgewählt, der im erstellten Testfall als Testwert dient. Zur Auswahl der Repräsentanten hat sich die Methode der Grenzwertanalyse bewährt.
3.3 Grenzwertanalyse
Erfahrungsgemäß finden Testfälle, die an den Grenzen von Wertebereichen testen, mehr Fehler als jene, bei denen beliebige Testwerte gewählt werden. Es handelt sich hierbei um typische um-eins-daneben Fehler, wie sie bei Schleifen häufig auftreten. Auch Feldvariablen (Arrays) sind anfällig für diese Art von Fehlern. Beginnt die Indizierung bei Null, entspricht der höchste erlaubte Index nicht der Länge des Feldes, sondern um eins weniger. Soll auf das erste Element zugegriffen werden, ist der entsprechende Index nicht eins, sondern null. Testwerte sollten genau an der Grenze und unmittelbar oberhalb bzw. unterhalb der Grenze gewählt werden. Abbildung 9 zeigt diesen Sachverhalt grafisch.
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 9: Grenzwertanalyse, Quelle [4]
Die Grenzwertanalyse kann als eigenständiges Testverfahren oder auch in Verbindung mit der Äquivalenzanalyse verwendet werden. Letztere Variante vereint die Vorteile beider Verfahren, indem nicht ein beliebiger Wert gewählt wird, sondern an den Grenzen der Partitionen getestet wird. [13] Die Grenzwertanalyse steigert ebenfalls die Effektivität der Category-Partition Methode, weil dieses Verfahren ebenfalls auf Äquivalenzklassen basiert.
3.4 Unit-Tests
Mithilfe von Modultests werden einzelne Programmteile getestet, statt das Programm als Ganzes zu testen. Modultests folgen damit dem Bottom-Up Ansatz. Erst wenn in den kleinsten Programmeinheiten (Units) keine Fehler mehr gefunden werden, werden sie zu größeren Programmteilen zusammengefügt. Aufgetretene Fehler sind in kleinen Einheiten viel leichter zu finden und zu beheben als in großen, weil der Umfang der Suche nur auf diese kleine Programmeinheit beschränkt bleibt.
Modultests bzw. Unit-Tests wurden durch den Einsatz in Extreme Programming (XP) populär. Mit den Akzeptanztests bilden sie die Basis für die Qualitätssicherung. Zum Beispiel sind zwei der zwölf Kernprinzipien von XP sind dem Testen gewidmet: [13]
- Fortlaufende Tests: Ein Modul ist erst fertig, wenn es die Unit-Tests besteht. Die fertige Software muss alle Unit-Tests und Akzeptanztests erfolgreich absolvieren.
- Fortlaufende Integration: Jeden Tag werden alle erfolgreich getesteten Module zusammengefügt.
Mithilfe der Unit-Tests lässt sich die Fehlersuche wesentlich beschleunigen und automatisieren. Die Tests lassen sich beliebig oft wiederholen und bieten so eine Art Sicherheitsnetz für den Softwareentwickler. Es lassen sich die bisher programmierten Module auf Knopfdruck testen, ob das Programm noch ordnungsgemäß funktioniert. Auch leichtfertige Änderungen am Programm werden auf diese Weise schnell aufgedeckt. [17]
4 Benutzerhandbuch
4.1 Einführung in TestIt
TestIt ist ein Werkzeug für den Black-Box-Test, das Sie beim systematischen Erstellen von Testfällen für den Unit- und Integrationstest unterstützt. Der Benutzer beschreibt dazu für jede zu testende Funktion die erlaubten Parameterwerte in Form von Äquivalenzklassen. TestIt erstellt daraus alle relevanten Testfälle. Der Benutzer muss nur noch für jeden dieser Testfälle das erwartete Ergebnis spezifizieren, abschließend kann er mit TestIt die Testfälle in unterschiedlicher Form (zum Beispiel als NUnit-Testmethoden im Quelltext oder als textuelle HTML-Dokumentation) ausgeben. Das Ausgabeformat kann durch Mustertexte, so genannte Quelltext-Vorlagen, variabel gestaltet werden. Durch den Gebrauch von Quelltext-Vorlagen ist das Programm unabhängig vom eingesetzten Testframework und der verwendeten Programmiersprache. Darüber hinaus ist die textuelle Beschreibung viel abstrakter als Quelltext. Somit können Sie die Testfälle bereits zu einem sehr frühen Zeitpunkt im Softwareentwicklungsprozess formulieren.
Falls die zu testende Klasse als Quelltext oder Assembly vorliegt, wird diese durch TestIt automatisiert analysiert und die Informationen über deren Eigenschaften, Methoden und Parameter (so genannte Metainformationen) extrahiert. Die Klasse selbst muss nicht vollständig implementiert sein. So genügt es beispielsweise, den Quelltext einer Klasse zu importieren, der nur aus den Methodenköpfen bzw. der Schnittstellenbeschreibung besteht. Natürlich können Sie die Metainformationen auch manuell eingeben. TestIt erzeugt aus den Metainformationen automatisch alle erforderlichen Testfälle mittels Äquivalenzklassenanalyse. Anschließend vervollständigen Sie die Testfälle und formulieren den Testablauf. Darüber hinaus können Sie die Tests direkt von TestIt ausführen lassen.
Dieses Benutzerhandbuch soll weder in das Thema Testen einführen noch Prinzipien des Testens erklären. Ein gewisses Vorwissen zu diesem Thema ist daher unerlässlich. Eine gute Einführung bietet das Buch "The Art of Software Testing" von G. Myers oder "Lessions Learned in Software Testing" von C. Kara, J. Bach und B. Bettichord. Der Aufbau der einzelnen Abschnitte ist so gewählt, dass in der Einleitung jedes Abschnitts die folgenden Unterabschnitte zusammengefasst werden bzw. eine Empfehlung abgegeben wird, wie dieser Abschnitt gelesen werden sollte.
In diesem Abschnitt werden folgende Themen behandelt:
- Systemvoraussetzungen: Dieser Abschnitt informiert Sie über die minimalen Systemvoraussetzungen für Hard- und Software, um TestIt einsetzen zu können.
- Installation: Dieser Abschnitt enthält eine Installationsanleitung für TestIt. Sofern Sie mit der Installation von Programmen vertraut sind, können Sie diesen Teil überspringen.
4.1.1 Systemvoraussetzungen
Hardware
Abbildung in dieser Leseprobe nicht enthalten
Software
Abbildung in dieser Leseprobe nicht enthalten
4.1.2 Installation
Damit Sie TestIt verwenden können, müssen Sie das Programm auf Ihrer Festplatte installieren. Um TestIt zu installieren, führen Sie bitte folgende Schritte aus:
1. Schließen Sie alle laufenden Programme.
2. Legen Sie die Installations-CD in Ihr CD-Laufwerk ein.
3. Vom Startmenü wählen Sie Ausführen.
4. Tippen Sie D:\setup (ersetzen sie D mit dem Laufwerksbuchstaben Ihres CD-Laufwerks).
5. Folgen Sie den Anweisungen auf dem Bildschirm.
Standardmäßig wird TestIt in das Verzeichnis C:\Programme\TestIt installiert.
4.2 Funktionsweise
Dieser Abschnitt erklärt die grundlegende Funktionsweise von TestIt. Selbst wenn Sie bereits mit der Äquivalenzklassenanalyse vertraut sind, benötigen Sie das hier vermittelte Wissen für den Einsatz von TestIt. Zum Durchführen von Tests sind Testdaten und eine Beschreibung des Testablaufes erforderlich. Es geht also um die Frage, wie die Testdaten zum Testen eingesetzt werden. Daher ist die Funktionsweise von TestIt in die folgenden zwei Teile gegliedert:
- Erzeugen der Testdaten: Testdaten sind die Grundlage für Tests. Hier erfahren Sie, wie der Algorithmus zum Erzeugen der Testdaten arbeitet.
- Erzeugen des Testablaufs: Im zweiten Schritt wird der Testablauf formuliert. Dieser beschreibt, wie die Testdaten für die Tests eingesetzt werden. Das Resultat aus Testdaten und Testlauf sind die Testklassen.
4.2.1 Erzeugen der Testdaten
Der Algorithmus zum Erzeugen der Testdaten basiert auf dem Artikel "The category-partition method for specifying and generating functional tests" von Thomas J. Ostrand und M. Balcer sowie dem Buch "The Art of Software Testing" von G. Myers.
Zusammengefasst besteht die Vorgehensweise zum Erstellen der Testdaten aus folgenden Schritten:
1. Finde die zu testenden Funktionen (Methoden).
2. Finde für jede Funktion alle Ein- bzw. Ausgangsgrößen (Faktoren).
3. Finde für jeden Faktor die dazugehörigen Äquivalenzklassen (Partitionen).
4. Wähle für jede Äquivalenzklasse einen Wert aus (zum Beispiel durch Grenzwertanalyse) und bilde die Testfälle durch Kombination der Faktorwerte.
- Gültige Faktorwerte sollten mit allen anderen gültigen Faktorwerten kombiniert werden.
- Ungültige Faktorwerte (Fehlerfälle, Sonderfälle) sollten nicht mit anderen ungültigen Faktorwerten kombiniert werden. Es genügt, sie einmal zu testen.
Für die Schritte 1 bis 3 werden Metainformationen benötigt. Die Metainformationen sind in diesem Fall Funktionen, Faktoren, Äquivalenzklassen und Testwerte. Eine Klasse im Sinne der objektorientierten Programmierung besteht aus Methoden. Diese Methoden wiederum aus Parametern. Diese drei Begriffe gilt es, mit den bereits erwähnten Metainformationen in Verbindung zu bringen. Daher entsprechen die Methoden den Funktionen bzw. Parameter den Faktoren. Nachstehendes Diagramm stellt die Zusammenhänge grafisch dar. Die hier fehlenden Eigenschaften sind vorerst nicht wichtig. Sie werden im Abschnitt Erstellen der Metainformationen aufgelistet.
Abbildung in dieser Leseprobe nicht enthalten
Die nachfolgende Abbildung zeigt beispielsweise die Metainformationen für die Klasse Util mit der Methode Power. Power potenziert die Zahl val mit dem Exponenten exp.
Abbildung in dieser Leseprobe nicht enthalten
Um aus den bisherigen Metainformationen Testfälle erzeugen zu können, fehlen noch konkrete Werte für die Faktoren. Da erschöpfendes Testen unmöglich ist, ist eine Vorgehensweise erforderlich, welche diese Unvollständigkeit weitgehend reduziert. Es soll also jene Teilmenge aller denkbaren Testfälle gefunden werden, welche die größte Wahrscheinlichkeit besitzt, möglichst viele Fehler zu finden. Der erste Schritt in diese Richtung ist das Bilden von Äquivalenzklassen. Statt unzählig viele mögliche Faktorwerte zum Testen heranzuziehen, wird im einfachen Fall nur ein Faktorwert repräsentativ für alle Werte in der Äquivalenzklasse verwendet. In der folgenden Abbildung wurden zu jedem Faktor Äquivalenzklassen und Testwerte hinzugefügt.
Abbildung in dieser Leseprobe nicht enthalten
Da Testfälle immer aus den Testdaten und dem erwarteten Ergebnis bestehen, wird ein weiterer Faktor eingefügt, der das Ergebnis aus dem Methodenaufruf repräsentiert. Der Ergebnisfaktor ist immer ein Ausgangsfaktor. Dieser ist durch die entgegengesetzte Pfeilrichtung gekennzeichnet.
Abbildung in dieser Leseprobe nicht enthalten
Neben der manuellen Eingabe der Metainformationen können diese auch automatisiert erstellt werden. Aus dem eventuell vorliegenden Quelltext oder Assembly extrahiert TestIt die Klasse, Methoden und Faktoren automatisiert. In Testspezifikationen besteht die Möglichkeit, zusätzlich Äquivalenzklassen und Testwerte anzugeben.
Die Spezifikation der Metainformationen sind somit komplett. Schritt 4 der Anleitung beschreibt, wie aus den spezifizierten Faktorwerten Testfälle erstellt werden. Im Standardfall werden die Faktorwerte mit den anderen Faktorwerten kombiniert. Jedoch werden nur solche Faktorwerte kombiniert, deren Faktor dieselbe Richtung aufweist. Also Eingangsfaktoren werden nur mit anderen Eingangsfaktoren kombiniert. Dasselbe gilt für die Ausgangsfaktoren.
Pro Testfall und dessen Testdaten ist jeweils das erwartete Ergebnis anzugeben. Da nicht jeder Testfall in gleicher Weise geeignet ist, Fehler zu finden, kann man jeden einzelnen Testfall aktivieren bzw. deaktivieren. Aus den bisher spezifizierten Metainformationen entstehen folgende vier Testfälle für die Methode Power:
Abbildung in dieser Leseprobe nicht enthalten
Somit sind die Testfälle komplett. Es handelt sich hierbei um ein sehr einfach gehaltenes Beispiel. Anspruchsvollere Beispiele mit ungültigen Testwerten und Spezialfällen finden Sie im Abschnitt Beispiele.
Jetzt fehlt noch der schrittweise Ablauf, wie diese Testfälle abgearbeitet werden. Mit diesem Thema beschäftigt sich der nächste Abschnitt.
4.2.2 Erzeugen des Testablaufs
Nachdem die Testfälle erstellt wurden, müssen diese zur Ausführung gebracht werden. Für die Ausgabe der Testfälle können Sie Quelltext-Vorlagen angeben, deren Platzhalter von TestIt später durch die Daten der einzelnen Testfälle ersetzt werden. Die Platzhalter sind dabei Textmarken, deren Name zwischen den Klammern <% und %> steht. Von TestIt sind bereits einige Marken, wie beispielsweise <% Index %>, vorgegeben. Vorlagen sind jeweils einer Klasse bzw. den zu testenden Methoden zugeordnet. Sie bilden also das Gerüst für die Testklasse bzw. einen Testfall. Nachfolgende Abbildung stellt diesen Vorgang grafisch dar.
Abbildung in dieser Leseprobe nicht enthalten
Der Aufbau einer Testmethode, wie zum Beispiel TestPower, folgt meist dem Schema:
1. Herstellen der Vorbedingungen: Erzeugen der Testdaten.
2. Test: Aufruf der zu testenden Methode.
3. Überprüfen der Nachbedingungen: Prüfen mittels Assertions, ob das erwartete mit dem tatsächlichen Ergebnis übereinstimmt.
Die Testmethode im unteren Teil der nachfolgenden Abbildung ist ebenfalls nach diesem Schema aufgebaut. Statt die Faktorwerte gesonderten Variablen zuzuweisen, könnten Sie direkt in den Methodenaufruf eingefügt werden. Diese längere Vorgehensweise soll verdeutlichen, dass das Herstellen der Vorbedingungen auch umfangreicher sein kann, wie es beispielsweise beim Erstellen von Auflistungen der Fall ist.
Abbildung in dieser Leseprobe nicht enthalten
Um die Testklassen zu erstellen, werden die Vorlagen mittels Textersetzung an den Stellen der Marken zusammengefügt. Sämtliche Testmethoden kommen an die Stelle der Marke Methods, die Faktorwerte an die Stelle der Marke welche den Faktornamen trägt. Zusätzlich wird der Name der Testmethode mit einer laufenden Nummer an der Marke Index versehen. Falls mehrere Testfälle vorliegen, dient die Nummer zur Unterscheidung der einzelnen Testmethoden. Eine Übersicht zu den verfügbaren Quelltext-Marken finden Sie in der Referenz.
Abbildung in dieser Leseprobe nicht enthalten
Das Beispiel hat gezeigt, dass es letztlich gleichgültig ist, welchen Text die Vorlage enthält. Der sehr simple Algorithmus ersetzt einfach die Marken mit Text. Die Semantik des Texts spielt für TestIt keine Rolle. Das Programm schränkt Sie daher nicht auf ein bestimmtes Testframework oder eine Programmiersprache ein. Folglich ist es kein Problem statt einer Testklasse einen HTML-Bericht zu erstellen. Die sich daraus ergebenden vielfältigen Möglichkeiten zeigen Ihnen die Beispiele in diesem Benutzerhandbuch.
Der folgende Abschnitt erläutert die Benutzeroberfläche von TestIt. Nach dem Durcharbeiten sind Sie in der Lage, die soeben gezeigten Schritte zur Erstellung der Tests praktisch umzusetzen.
4.3 Benutzeroberfläche
Im oberen Fensterbereich der Benutzeroberfläche von TestIt befinden sich die Menü- und Symbolleiste. Mittels stichwortbezogener Hilfefunktion können Sie auf einfache Art und Weise eine Volltextsuche in den Hilfethemen durchführen. Die Statusleiste befindet sich im unteren Bereich des Fensters.
Der Arbeitsbereich von TestIt ist dreigeteilt. Links bzw. unten befindet sich jeweils ein Bereich für andockbare Fenster. Insgesamt gibt es drei dieser Fenster:
- Klassenansicht: Zeigt hierarchisch die Metainformationen der zu testenden Klasse.
- Mitteilungen-Fenster: Während Sie arbeiten, informiert Sie TestIt über aufgetretene Fehler.
- Ausgabe-Fenster: Zeigt den Standardausgabestrom der laufenden Tests.
Nachstehende Abbildung zeigt die Unterteilung des Hauptfensters:
Abbildung in dieser Leseprobe nicht enthalten
Wesentliche Eingabeelemente im primären Arbeitsbereich sind:
[...]
-
-
-
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X. -
-
Upload your own papers! Earn money and win an iPhone X.