Читать книгу Softwaretest in der Praxis - Stefan Schmerler - Страница 5
Оглавление1 Grundlagen des Testens
Bei der Herstellung aller industriellen Güter gibt es konkrete Anforderungen an die Qualität. Das grundsätzliche Vorgehen bei der Sicherstellung dieser Qualität ist oft die Entnahme von Stichproben, an denen dann geprüft wird, ob alle gestellten Anforderungen erfüllt werden oder ob Abweichungen existieren. Versteht man Software als solches industrielles Gut, so entspricht die Sicherstellung dieser Qualitätsmerkmale hier einem Softwaretest.
Software ist immateriell.
Im Gegensatz zu materiellen Gütern ist Software allerdings immateriell, sie kann nicht angefasst und einer materiellen Prüfung unterzogen werden. Lediglich eine Art optische Prüfung ist in eingeschränktem Umfang durch intensives Gegenlesen der Entwicklungsdokumente möglich.
die Folgen fehlerhafter Software
Dabei können die Folgen fehlerhafter Software durchaus erheblich sein, von einer verspäteten Markteinführung bis zu existenzbedrohenden finanziellen Verlusten und Gefahren für Leib und Leben. Auch ein Imageschaden kann eine negative Konsequenz sein. Beispiele hierfür können der Tagespresse entnommen werden wie z. B. eine fehlerhafte teilautonome Fahrfunktion (»Autopilot«).
stichprobenhafter Charakter
Der Test von Software ist in der Regel eine stichprobenhafte Prüfung durch Ausführung der Software anhand von Testfällen. Die Durchführung eines Testfalls bedeutet hierbei die Ausführung des Testobjekts (der zu testenden Software) anhand spezifischer, durch den Testfall definierter Testdaten (Stimuli).
dynamischer und statischer Test
Je nach Ausprägung des Tests unterscheidet man zwischen dynamischem und statischem Test. Während beim dynamischen Test das Testobjekt ausgeführt wird, erfolgt ein statischer Test durch Prüfung der relevanten Dokumente wie der Anforderungsspezifikation (Lastenheft) der Software oder des Quellcodes.
Validierung und Verifikation
Bei der Frage, ob Anforderungen erfüllt werden, ist relevant, von welcher Seite diese erhoben werden. Handelt es sich um die Prüfung der Anforderungen im Lastenheft, spricht man von einer Verifikation. Die Prüfung, ob sich das System in der späteren Zielumgebung entsprechend den Vorstellungen der Nutzer verhält, wird hingegen als Validierung bezeichnet.
Software ist (fast)
immer fehlerhaft.
Viele Jahre praktischer Erfahrung in der Softwareentwicklung führen leider zu der Erkenntnis, dass fast jede nichttriviale Software fehlerhaft ist. Die Gründe hierfür sind vielfältig. Häufig werden während der Entwicklung und beim Testen bestimmte Ausnahmesituationen nicht bedacht oder überprüft. Seien es etwa eine eigentlich nicht vorgesehene Eingabe des Anwenders oder sehr seltene Systemzustände, die dann aber doch im Laufe der Softwarenutzung eingenommen werden. Fehler kommen auch zustande bei großem Zeitdruck oder hoher Komplexität der zu lösenden Entwicklungsaufgabe oder eingesetzten Technologien. Auch Missverständnisse zwischen den Projektbeteiligten z. B. in Bezug auf Schnittstellen sind häufige Fehlerursachen.
Mit Testen kann man keine
fehlerfreie Software erzielen.
Eine weitere bittere Erkenntnis, die offensichtlich aus dem stichprobenhaften Charakter des Softwaretests folgt: Auch wenn alle durchgeführten Tests keine Fehler mehr aufdecken, kann nicht ausgeschlossen werden, dass andere Tests bislang unerkannte Fehler entdecken würden. Durch Testen kann keine Fehlerfreiheit nachgewiesen werden!
Test und Entwicklung
Der Test von Software und eingebetteten Systemen kann, über den Lebenszyklus betrachtet, als der teuerste, aber auch der am meisten unterschätzte Entwicklungsschritt betrachtet werden. Für die ersten 50 % der Entwicklungsarbeit wie Analyse, Spezifikation oder Implementierung steht ein hervorragender Methoden-, Werkzeug- und Technologiebaukasten zur Verfügung. Wir haben z. B. werkzeugunterstützte Anforderungserfassung und modellbasierte Funktionsentwicklung bis hin zum Fehlermanagement mit vollständiger Durchgängigkeit sogar in großen Konzernen.
Fokus auf den linken Ast
im V-Diagramm
Von der anderen Hälfte der Entwicklungsarbeit – dem Testen – kann man das leider nicht behaupten. Die testmethodische Begleitung wird oftmals stiefmütterlich behandelt – Ausnahmen bestätigen natürlich die Regel. Oftmals wird es der persönlichen Intuition des Entwicklers anheim gestellt, welche Tests durchgeführt werden und welcher Testaufwand und Testabdeckungsgrad vorgesehen werden. Anstelle von klaren Testende-Kriterien tritt ein »gutes Bauchgefühl« und auch die Wahl der Testtechnologie scheint in der Praxis oft willkürlich oder zufällig. Diese Aussagen beziehen sich nicht auf spezifische Industriedomänen oder Anwendungen. Sie beziehen sich auch nicht auf die Entwicklung sicherheitskritischer Systeme, für die besondere Regelungen und Absicherungskonzepte sogar gesetzlich vorgeschrieben sind, deren Einhaltung in Form von Audits auch überprüft wird. Dennoch sind die beschriebenen Phänomene immer noch viel zu oft in der Praxis anzutreffen.
Das Schöne am Testen ist: Man
kann jederzeit damit aufhören –
die Rechnung kommt später.
Eine Ursache hierfür könnte sein, dass mit dem Testen keine unmittelbar ersichtliche Wertschöpfung verbunden ist. Man kann letztlich jederzeit einen Testprozess abbrechen, die Funktion scheint vordergründig ja gegeben. Häufig wird dieser Umstand auch genutzt, um die am Ende fehlende Projektlaufzeit an einen schleppenden Gesamtprojektverlauf »anzupassen«. Oder positiv formuliert: Das Schöne am Testen ist, dass man jederzeit damit aufhören kann. Die Rechnung kommt später …
eine Metrik zur Erfassung
des Reifegrads von Software
Um diesem Problem begegnen zu können, müssen Aussagen über den Reifegrad einer Software getroffen werden – idealerweise durch Nutzung einer Metrik, die reproduzierbare Ergebnisse liefert. Nur auf diese Art und Weise kann ein Testende-Kriterium sinnvoll festgelegt werden.
eine Chronik historischer Softwarefehler
Ferner brauchen Entwickler und Testingenieure einfach anwendbare Methoden und Werkzeuge, die aufgrund der dann hoffentlich hohen Akzeptanz auch tatsächlich zur Anwendung kommen. Hochkomplexe Expertenwerkzeuge oder Testtechnologien laufen Gefahr, im Entwickleralltag nicht oder nur formal eingesetzt zu werden, um gewisse Anforderungen im Entwicklungsprozess zu erfüllen. Die Erfahrung zeigt: Für viele Testprobleme genügt bereits eine einfache, methodisch untermauerte Vorgehensweise, um mit vergleichsweise niedrigem Testaufwand einen signifikanten Reifegradgewinn zu erzielen.
Die Geschichte der Softwareentwicklung seit Anfang der 1970er Jahre ist so umfangreich wie die Geschichte ihrer katastrophalen Fehler – wobei letztere sich sogar noch besser in Erinnerung halten.
1.1 Fehlerbegriff
Zu Beginn ist es wichtig, die Definition einiger Begriffe festzulegen, die im weiteren Verlauf dieses Buchs Verwendung finden. Allem voran natürlich der Begriff des Fehlers selbst.
Ein Fehler ist die nachgewiesene Nichterfüllung einer bekannten Anforderung, d. h. die Abweichung des Istverhaltens vom Sollverhalten einer Software.
Testbasis
Diese Abweichung kann natürlich nur dann ermittelt werden, wenn vorab das richtige Verhalten korrekt spezifiziert wurde. Man spricht in diesem Zusammenhang von der Testbasis, gegen die getestet wird.
Ein Fehler äußert sich in der Regel im Verhalten der Software, dabei unterscheidet man zwischen Fehlerwirkung und Fehlerzustand.
Unter Fehlerwirkung (auch Failure, Fehlverhalten, Fehler) versteht man die äußeren Anzeichen eines Softwarefehlers, die Ursache hingegen als Fehlerzustand (auch Defect, Fault, Bug) bezeichnet (DIN 66271).
»Ein Unglück kommt selten allein«, wie ein Sprichwort sagt. In der Tat zeigen Analysen, dass Fehler häufig aggregiert auftreten, d. h., wo ein Fehler aufgetreten ist, kann man mit höherer Wahrscheinlichkeit davon ausgehen, noch weitere Fehler zu finden. Unter Umständen verursacht die Behebung eines Fehlers auch erst die Wirkung eines weiteren Fehlers, der vorher bereits im Code enthalten war, durch den ersten Fehler allerdings nicht zur Wirkung kam. Man spricht von Fehlermaskierung.
Fehlerzustände können sich gegenseitig kompensieren oder überdecken. Eine Fehlerwirkung tritt dann erst nach Behebung der maskierenden Fehlerzustände zutage.
Ergebnisse klassifizieren:
False positive, false negative,
true positive, true negative
Nicht bei jedem Auftreten eines unerwarteten Verhaltens kann jedoch von einem Fehler des Testobjekts ausgegangen werden. Ein Missverständnis in der Spezifikation oder Fehler in der Testumgebung können ebenfalls in einem vermeintlichen Fehler resultieren. In diesem Fall spricht man von einem falsch positiven (false positive) Resultat. Wird umgekehrt ein vorhandener Fehler nicht entdeckt, obwohl ein entsprechender Test dafür vorliegt, haben wir ein falsch negatives (false negative) Resultat. Von einem richtig positiven (true positive) Ergebnis spricht man, wenn die Fehlerwirkung durch den Test gefunden wird und umgekehrt bedeutet ein richtig negatives (true negative) Resultat, dass ein erwartetes Verhalten des Testobjekts mit dem Test nachgewiesen werden konnte.
Ursachen für die Ent-
stehung von Fehlern
So wichtig wie das Auffinden von Fehlerzuständen ist die Kenntnis der Ursachen ihrer Entstehung. Beim ersten jemals dokumentierten Bug handelte es sich tatsächlich um eine Motte, die am 9. Sept. 1947 in Relay #70 des Panels F eines Mark-II-Computers in Harvard von zwei Relaiskontakten erschlagen wurde. Der erste Bug Report der Computergeschichte wurde daraufhin noch am selben Tag durch die Universitätsmitarbeiterin Grace Hopper erstellt und enthielt unter einem Klebestreifen die Fehlerursache.
der erste Bug Report der Computergeschichte (9. Sept. 1947)
nebst angeklebter Fehlerursache. Bild: Gemeinfrei/CC0
Inzwischen sind Fehlerursachen nicht mehr so gegenständlich und leicht zu finden. Insbesondere in der industriellen Softwareentwicklung belegen die Erfahrungen mehrerer Dekaden die folgenden Hauptursachen:
Zeitdruck
Der Zeitdruck in Projekten führt zu Nachlässigkeiten in der Softwareentwicklung.
Missverständnisse
Die Projektpartner besitzen unterschiedliches Verständnis über Sachverhalte, Aufgabenstellungen oder Anforderungen. Diese Missverständnisse führen oft zu Fehlern.
Komplexität
Größere Softwareprojekte benötigen aktive Maßnahmen zur Sicherstellung von Softwarequalität, z. B. eine stringente Architekturprüfung oder Einhaltung von Codierrichtlinien. Andernfalls entsteht ein Monolith, den kein Entwickler mehr versteht und ohne fehlerträchtige Seiteneffekte ändern oder erweitern kann.
Integration
Bei größeren Projekten, die die Integration vieler Komponenten erfordern, besteht auch in diesem Schritt großes Potenzial für Missverständnisse, da wiederum verschiedene Parteien über viele Sachverhalte ein identisches Verständnis besitzen müssen.
neue Technologien
Immer dann, wenn Entwicklungsprojekte einen Technologieschub erfahren, ist dies eine potenzielle Fehlerquelle. Neue Technologien müssen vor der korrekten Anwendung erst gut verstanden werden.
1.2 Testen von Software
Auch im Bereich des Testens gilt es, zuallererst einige Begrifflichkeiten zu definieren.
1.2.1 Testbegriff
Testen ist nicht Debuggen!
Hier wäre z. B. der Begriff des Debuggens, der oft fälschlicherweise synonym zu Testen verwendet wird. An dieser Stelle sei gesagt: Testen ist nicht Debuggen! Während Debuggen das Ziel hat, Fehlerzustände zu lokalisieren und zu beheben d. h. Fehler zu beseitigen, ist es Aufgabe des Tests, Verhaltensweisen (Fehlerwirkungen) aufzudecken, die auf Fehlerzustände hindeuten, d. h. Fehler zu finden.
Regressionstest
Nach einer Fehlerbeseitigungsmaßnahme ist über einen nachgelagerten Test noch zu verifizieren, dass zum einen der Fehler tatsächlich nicht mehr auftritt und zum anderen keine neuen Fehler durch die Maßnahme in den Code eingebracht wurden. Derartige Tests werden als Regressionstests bezeichnet.
die wichtigsten Ziele beim
Testen von Software
Ein Softwaretest kann unterschiedliche Ziele verfolgen und aus verschiedenen Motivationen heraus erfolgen. Die wichtigsten Testziele in der Praxis werden im Folgenden kurz dargelegt:
▶die Überprüfung, dass alle im Lastenheft beschriebenen Anforderungen funktional korrekt umgesetzt wurden,
▶die qualitative Bewertung verschiedener testrelevanter Dokumente wie Lastenhefte oder Quellcode,
▶die Begrenzung des aus einem schlechten Reifegrad resultierenden wirtschaftlichen Risikos,
▶die Analyse des Quellcodes und der assoziierten Dokumente, um Fehlerzustände zu vermeiden und vorhandene Defects zu beseitigen,
▶der Nachweis der Konformität der Software zu gesetzlichen, juristischen oder vertraglichen Standards und Anforderungen,
▶Freigaberelevante Informationen erhalten, z. B. für die nächste Integrationsstufe, oder um den Code in eine konsolidierte Release einzuchecken.
Testbewertung, System under Test
und Auffälligkeit
Ein Test ist eine stichprobenhafte Prüfung. Man führt das Testobjekt aus und untersucht, ob es sich korrekt verhält, z. B. durch Vergleich mit einer Spezifikation, aus der das gewollte Verhalten hervorgeht. Entsprechend wird der Test mit Pass/Fail bewertet. Stellt man ein abweichendes Verhalten des Testobjekts fest (oft auch System under Test bzw. SUT genannt), ist sich aber noch nicht sicher, ob Fehlverhalten oder z. B. ein Spezifikationsfehler, eine Spezifikationslücke oder gar ein Fehler im Testsystem vorliegt (der Umgebung, die den Test durchführt), spricht man bis zur eindeutigen Klärung auch neutral von Auffälligkeit. Die Durchführung dieser Klärung wird in aller Regel von Spezialisten der Fehleranalyse durchgeführt – dies kann auch der Entwickler selbst sein. Es ist dazu in jedem Fall tiefe Expertise erforderlich.
Das Testendekriterium - wann
kann man aufhören zu testen?
Der stichprobenhafte Charakter des Tests hat eine Reihe von Konsequenzen, die im Folgenden erläutert werden. Es gibt kein klar erkennbares, allgemeingültiges Kriterium für das Testende. Man testet oftmals so lange, bis das Vertrauen in die Software groß genug ist, um sie zum kommerziellen Einsatz zu bringen. Dies ist bei der Steuerungssoftware einer Kaffeemaschine sicher anders als bei der Kühlelektronik eines Kernkraftwerks oder der Klappensteuerung eines Flugzeugs. Andererseits muss man sich immer im Klaren darüber sein, dass auch Kaffeemaschinen in hohen Stückzahlen produziert werden. Wird ein kapitaler Fehler erst beim Kunden entdeckt, entstehen bei einfachen, aber in hohen Stückzahlen gefertigten Systemen sehr schnell ganz erhebliche Garantiekosten. Der Aufwand, den man bei einer Softwareentwicklung in den Test investieren sollte, hängt also vom möglichen Fehlerschaden und dessen Eintrittswahrscheinlichkeit ab. Doch dazu später mehr.
Tests sind immer Stichproben ohne
implizites Testendekriterium
Es bleibt festzuhalten, dass Tests einen stichprobenhaften Charakter aufweisen. Selbst bei einer hohen Anzahl fehlerfreier Tests und dem hieraus womöglich resultierenden Testende kann man nie sicher sein, dass nicht bereits der nächste Test einen kapitalen Fehler zutage gefördert hätte! Man benötigt also dringend eine Metrik, die es erlaubt, Qualitätsaussagen oder eine (noch verbleibende) Fehlerneigung numerisch auszudrücken und auf dieser Basis eine fundierte, nachvollziehbare Testende-Entscheidung zu treffen. Auch hierzu mehr im weiteren Verlauf dieses Buchs.
Testmanagement
Doch zurück zur weiteren Klärung des Testbegriffs. Zum Testprozess gehören neben der Ausführung des Testobjekts (z. B. einem eingebetteten System, Funktionsmodell oder einer Bürosoftware) mit Testdaten (Eingabedaten/Stimuli eines zu testenden Programms) auch die Planung, Durchführung sowie die Auswertung/Bewertung der Tests und die Verwaltung aller anfallenden Daten. All diese Vorgänge subsumiert man unter Testmanagement.
Testlauf, Testfall und Testszenario
Ein Testlauf (Test Run) umfasst die Ausführung mehrerer Testfälle. Unter Testfall ist eine konkrete Testdurchführung, d. h. Ausführung des Testobjekts auf einem Rechner mit spezifizierten Testdaten zu verstehen. Zu einem Testfall gehören also exakt festgelegte Randbedingungen. Testfälle können zu Testszenarien aneinandergereiht werden, wobei das Ergebnis eines Testfalls als Eingabe für den folgenden Testfall genutzt werden kann.
Testbedingung
Da eine Testbasis in der Regel eine Fülle an Anforderungen enthält, muss eine strukturierte Aufteilung auf mehrere Testfälle erfolgen. Jeder Testfall fokussiert sich dabei auf bestimmte Teilaspekte der Testbasis, woraus jeweils ein Satz von Testbedingungen (Test Conditions) abgeleitet wird. Jede dieser Testbedingungen ist durch einen oder mehrere Testfälle abzuprüfen.
Testsuite und Testzyklus
Zur weiteren Testausführungsorganisation werden Testsuites eingeführt. Unter einer Testsuite ist die geordnete Aneinanderreihung mehrere Testfälle zu verstehen, die innerhalb eines Testzyklus in einem definierten zeitlichen Muster für die Durchführung eingeplant werden.
Testskript
Im nächsten Schritt werden diese Testsuites dann automatisiert mithilfe von Testskripts ausgeführt. Testskripts sind die ersten Implementierungsinstanzen z. B. in Form ausführbarer Dateien. Hierbei werden alle Testfälle einer Testsuite mit der notwendigen Vor- und Nachbereitung, z. B. der Herstellung der für den Test erforderlichen Vorbedingungen, zur Ausführung gebracht.
Testprotokoll
Nach erfolgter Testdurchführung sind alle Ergebnisse in Testprotokollen zu erfassen, die nicht nur die direkten Testergebnisse, sondern auch alle zum Zeitpunkt der Testausführung geltenden, relevanten Randbedingungen dokumentieren.
Testkonzept
All diese Testaktivitäten und -methoden sowie Vor- und Nachbereitungen werden vor Testbeginn in einem sog. Testkonzept festgelegt. Dieses Dokument beinhaltet u. a. die Beschreibung der zum Einsatz kommenden Testverfahren und Testobjekte, die Festlegung der Testziele, die Art der Testberichterstattung sowie die gesamte Koordination der Testaktivitäten. Gerade der Dokumentation der Vorbereitungsmaßnahmen und Randbedingungen kommt beim Test eine besondere Bedeutung bei:
Ein Test ohne exakte Erfassung aller relevanten Randbedingungen, die zur Zeit des Tests vorlagen, ist nutzlos.
Alle Randbedingungen sind zu er-
fassen und zu dokumentieren!
Dies gilt zum einen, weil der Test andernfalls nicht exakt reproduziert werden kann (z. B. um die Fehlerbeseitigung zu verifizieren), und zum anderen, weil eine spätere Fehleranalyse dann oftmals unmöglich wird. Wir erinnern uns: Die Fehlerwirkung festzustellen ist eine Sache, die genaue Fehlerursache zu ermitteln, eine andere. Hier haben wir es häufig mit Wirkungsketten zu tun, die an einer Stelle initiiert werden und sich an einem gänzlich anderen Ort als Fehlverhalten niederschlagen. Für diese Analyse ist in der Regel die Kenntnis aller Randbedingungen und Details erforderlich.
Fehlerwirkungen treten oft nicht dort in Erscheinung, wo der Fehler verursacht wurde und behoben werden muss.
Fehleranalyse
Die Tätigkeit der Fehleranalyse gleicht akribischer Detektivarbeit, die viele Daten und große technische Expertise benötigt. Häufig ist sie nicht Bestandteil des eigentlichen Testprozesses.
Wie entsteht Komplexität und was
bedeutet das für den Softwaretest?
Bei der weiteren Beschreibung der Gegebenheiten beim Testen von Software muss auf den Begriff der Komplexität eingegangen werden. Wie entsteht Komplexität und welche Konsequenzen hat sie bei Entwicklung und Test von Software?
Stellen wir uns als einfaches Beispiel einen primitiven Rechner vor, bestehend aus einer Benutzerschnittstelle und einer kleinen Recheneinheit, die laut Lastenheft die Aufgabe hat, eine Zahl durch das Produkt zweier weiterer Zahlen zu dividieren.
Die dargestellt Routine wird gestartet, sowie das Gleichheitszeichen angeklickt wird. Und das war es schon, die Benutzerschnittstelle ist getrennt von der Kernfunktionalität und wir haben einen einfachen, übersichtlichen Code vor uns.
Sonderfälle und Fehlerbe-
handlung berücksichtigen
Aber: Dies ist ein außerordentlich fehlerhafter Code! Warum? Was geschieht z. B. bei der Aufgabenstellung 7 / (2 . 0) ? Das Programm gerät in einen undefinierten Zustand, es »stürzt ab« aufgrund einer Division durch null.
Wie ist dies zu verhindern? Das Programm muss die Faktoren im Divisor abfragen und bei einer Null eine Fehlermeldung ausgeben. Dies steigert, wie man sagt, die Robustheit des Programms.
nie darauf verlassen, dass Stimuli
spezifikationskonform erfolgen
Und ist der Programmcode nun fehlerfrei? Nein! Was geschieht, wenn ein Benutzer Buchstaben statt Ziffern eingibt? Das Programm gerät wiederum in einen undefinierten Zustand, wenn eine Berechnung mit Buchstaben vom Programmcode nicht behandelt wird. Schauen wir uns den hieraus resultierenden Code an.
Die reine Funktion ist der geringste
Teil eines hochwertigen Codes aus.
Und ist das Programm denn jetzt fehlerfrei? Sie ahnen es: Nein, es enthält nach wie vor Fehler, die zum Absturz führen können. So hat jeder Rechner Größenbegrenzungen für Zahlen, z. B. wird ein Integer-Wert bei einem Prozessor mit 16 Bit und bei einem anderen mit 32 Bit bearbeitet. Je nach Größe des Zahlenwerts, den man in die GUI eingibt, werden also Speicherbereiche überschrieben – und wieder haben wir ein Problem!
Noch kritischer als falsche An-
forderungens sind vergessene!
Sukzessive ist der Code auf mögliche Fehlerzustände zu untersuchen, die durch fehlerhafte, unplausible oder auch einfach nicht spezifizierte und dann ungünstig verfügte Testdaten hervorgerufen wurden. Und glauben Sie mir: Softwarenutzer sind sehr kreativ im Aufspüren solcher problematischen Daten! Wie so oft haben auch diese Fehlerquellen ihre Wurzeln in unvollständigen Spezifikationen, die dann korrekt umgesetzt wurden. Da keine konkrete Anforderung, wie z. B. die Forderung ganzzahliger Werte, verletzt wurde – es gibt ja keine! – ist es einem funktionalen Testverfahren, wie wir später noch sehen werden, auch nicht möglich, diesen Fehler zu finden.
Einen Code »wasserdicht« zu
machen ist sehr viel Arbeit!
Was zeigt dies alles? Ausgangspunkt war eine Zeile Programmcode. Der Programmcode enthält jetzt zusätzliche 16 Zeilen und ist immer noch fehlerhaft. Die Kernfunktionalität macht den geringsten Teil im Code aus. Einen fehlerfreien Code zu schreiben, ist sehr anspruchsvoll und für komplexe Programme praktisch unmöglich. Kein Programmierer kann alle Nutzungsszenarien antizipieren, aber fehlerfreie Software erfordert dies. Fehler sind Bestandteil jeder Software. Dies ist eine bittere Erkenntnis.
Die Frage ist nicht, ob Software fehlerfrei ist – sie ist es nicht – sondern, ob und wie gut man mit den Fehlern leben kann und ob diese tolerierbar sind.
einige Mythen über Software
Von Software wird allgemein gesagt, sie sei einfach zu erstellen und einfacher zu ändern als andere Gewerke. Man schließt von der physikalischen Welt auf gleiche Umstände bei Software – dies ist natürlich ein Trugschluss. Hier zwei trügerischen Annahmen:
▶»Software ist einfach zu erstellen, da jedes Schulkind, das Basic kennt, so etwas kann.«
▶»Software ist einfacher zu ändern als andere Gewerke, man denke nur an Gussbeton oder Stahlröhren!«
Die Erwartung, komplexe Software verhalte sich nach physikalischen Gesetzmäßigkeiten, ist bei Nichtfachleuten zwar weit verbreitet, aber ganz offensichtlich falsch. Insbesondere die Aufwände für den Test und die Mechanismen der Komplexitätssteigerung folgen besonderen Gesetzmäßigkeiten.
Aufwand und Komplexität
Wie aufwendig ist es also, einen Programmcode zu testen und wovon hängt dieser Aufwand ab? Einfache Antwort: Von der Komplexität des zu testenden Programms!
Betrachten wir den Testaufwand am Beispiel des Kontrollflussgraphen eines Programms in Abb. 1-1. Die Knoten des Kontrollflussgraphen repräsentieren unverzweigte Codesequenzen, die dargestellten Verzweigungen können z. B. durch if-then-else-Konstrukte oder Schleifen verursacht werden.
Abb. 1-1: Kontrollflussgraph eines Beispielprogramms
Der Testaufwand korreliert mit
der Komplexität von Software.
Ganz offensichtlich korreliert der Testaufwand mit der Komplexität der getesteten Software. Je komplexer die inneren (z. B. Verzweigungs-)Strukturen sind, desto aufwendiger ist der Test, um einen vergleichbaren Reifegrad zu erzielen. Um also Aussagen über den zu erwartenden Testaufwand treffen zu können, ist es unbedingt erforderlich, konkrete Informationen über die Komplexität der zu testenden Software zu besitzen. Eine Metrik für die numerische Messung von Softwarekomplexität wird später noch vorgestellt.
Grundsätzlich kann man sagen, dass Software nicht komplexer sein sollte, als es für die Lösung der zugrunde liegenden Aufgabe unbedingt erforderlich ist. Ein einfach strukturierter, leicht verständlicher Code hat nachweislich eine geringere Fehlerneigung als verflochtene, tiefverschachtelte Programme mit überladenen Anweisungen und Bedingungen.
statistische Daten
zu Softwarefehlern
Viele statistische Untersuchungen haben sich bereits mit der Fehlerhäufigkeit in der Softwareentwicklung auseinandergesetzt. Der Coverity Scan Report z. B. untersucht regelmäßig kommerzielle und Open-Source-Projekte und ermittelt Fehlerstatistiken. Für den Report 2014 wurden ca. 10 Mrd. Codezeilen mit folgendem Ergebnis analysiert:
▶Open-Source-Projekte weisen eine Gesamtfehlerdichte von 0,61 Fehlern je 1000 Codezeilen auf, für kommerzielle Projekte wurde ein Wert von 0,76 Fehlern je 1000 Codezeilen ermittelt.
▶Ein Projekt von 500.000 Codezeilen beinhaltet also statistisch 350 Fehler.
▶Die Zeit, um einen Fehler manuell zu finden und zu beheben, wird mit durchschnittlich 8–10 Stunden abgeschätzt.
1.2.2 Warum also ist Testen so schwierig?
keine Analogie zur Mechanik
in Qualität und Komplexität
Einer der Gründe, warum ein Softwaretest im Vergleich zur Prüfung mechanischer Komponenten als vergleichsweise schwierig erscheint, ist die Tatsache, dass die meisten Analogien aus dieser »bekannten« Welt versagen. Im Vergleich zu physikalischen Systemen gibt es z. B. kein theoretisches Modell, das es erlaubt, die Zuverlässigkeit eines Softwaresystems von der Zuverlässigkeit seiner Komponenten abzuleiten. Offensichtliche, plausible Qualitätskriterien wie für mechanische Bauteile gibt es für Software nicht. Zu viele Kriterien existieren, die man als Grundlage für diese Aussage heranziehen könnte.
Welchen Aussagewert besitzen die folgenden häufig (aber völlig zu Unrecht) zitierten Metriken in Bezug auf Softwarequalität?
▶Fehler pro Lines of Code sind denkbar ungeeignet. Die Fehlerhäufigkeit im Code ist eine Eigenschaft der Software und sagt nichts über deren Ausfallwahrscheinlichkeit aus noch gibt sie Aufschluss über die Kritikalität der Fehler. Diese ist von der Benutzung der Software abhängig.
▶Fehleraufdeckungsrate pro Zeit ist ebenfalls untauglich: Dies ist noch nicht einmal eine Messung von Softwareeigenschaften. Es ist eine Messung der Ausdauer, Vorstellungskraft und Intuition der Testergruppe. Der Test wird beendet, wenn den Testern die Ideen ausgehen.
Ohne Qualitätsmetriken auch
kein klares Testende-Kriterium!
Schwierig ist letztlich das Fehlen klarer Testende-Kriterien u. a. in Ermangelung adäquater Qualitätsmetriken. In der Praxis wird dem Testbestreben der Entwickler oft aufgrund wirtschaftlicher Randbedingungen ein Ende gesetzt. Die Terminsituation bei der Produktentwicklung erlaubt es zudem oftmals nicht, Deadlines zu verschieben, sodass auch dies den Testprozess (den letzten Prozessschritt der Entwicklung) vorzeitig beenden kann. Es gilt also immer, einen wirtschaftlichen Kompromiss zwischen Qualität und Kosten zu erzielen – oder, um mit Harry M. Sneed zu sprechen:
»Zuviel Qualität ist Luxus, zu wenig Qualität unverantwortlich«
Spannungsdreieck
Qualität-Kosten-Zeit
Die Grafik Abb. 1-2 zeigt das Spannungsdreieck Qualität-Zeit-Kosten, das diesen Sachverhalt recht gut veranschaulicht. Je früher der Testabbruch, desto höher die Folgekosten z. B. aufgrund von Garantieforderungen und Kulanz. Fehler, die im Produkt an den Kunden ausgeliefert werden, sind sehr teuer! Andererseits sind die Aufwände für das Testing bei frühem Testabbruch natürlich geringer. Je nach der Wirkung des Fehlers wird er vom Kunden nicht toleriert oder er ist sicherheitskritisch und kann nicht in regulären, ohnehin stattfindenden Wartungsmaßnahmen bereinigt werden.
Abb. 1-2: Das Spannungsdreieck Qualität-Kosten-Zeit
Steigert man den Qualitätsgrad über das wirtschaftlich sinnvolle Maß hinaus, steigen natürlich die Testkosten, während die Garantie-/Kulanzkosten sinken. Da sich die Gesamtkosten aus Aufwänden für die Absicherung und für Garantie/Kulanz zusammensetzen, ergibt sich ein Sweet Spot (Abb. 1-2), das Minimum der Kostenparabel. Diesen Sweet Spot gilt es aus betriebswirtschaftlicher Sicht anzufahren.
Abhängigkeiten von Testzeit, Test-
kosten, Testqualität und Testumfang
Harry Sneed hat die Abhängigkeit von Zeit, Kosten, Qualität und Umfang beim Softwaretest bereits 1987 im sog. »Teufelsquadrat des Tests« auf anschauliche Weise grafisch dargestellt (Abb. 1-3). Seine sehr realistische Grundannahme war, dass in Software-Entwicklungsprojekten sowohl die zur Verfügung stehende Zeit als auch das Budget begrenzt sind. Bei einem im Projektverlauf z. B. durch Spezifikationsänderung wachsenden Testumfang bedeutet dies zwangsläufig eine sinkende Testqualität und damit einhergehend in aller Regel auch eine schlechtere Produktqualität. Eine höhere Testqualität kann bei gleichem Testumfang nur mit höheren Testkosten und einer längeren Testzeit erreicht werden. Diese Abhängigkeiten ergeben sich durch Ziehen einer der Ecken des inneren Quadrats in Abb. 1-3. Diese Ecken muss man sich dabei als bewegliche Punkte auf den Diagonalachsen des äußeren Quadrats vorstellen.
Abb. 1-3: Teufelsquadrat des Tests
Planung des zu inves-
tierenden Testaufwands
Auch Pol kommt in [2] zu der Erkenntnis: »Testen ist ökonomisch sinnvoll, solange die Kosten für das Finden und Beseitigen eines Fehlers im Test niedriger sind als die Kosten, die mit dem Auftreten eines Fehlers bei der Nutzung verbunden sind.« Der Testaufwand ist also immer in Abhängigkeit von dem mit dem Fehlerfall verbundenen Risiko zu sehen. Das wirtschaftliche Risiko wiederum ergibt sich als Produkt der potenziellen Schadenshöhe mit der Fehlereintrittswahrscheinlichkeit.
Bei sicherheitskritischen Sys-
temen gelten andere Regeln.
Anders verhält es sich, wenn noch weitere Faktoren eine Rolle spielen, z. B. Sicherheitsaspekte. Wenn Leib und Leben von Menschen auf dem Spiel stehen, sind a priori die größten sinnvoll darstellbaren Aufwände zu treiben. Hier macht der Gesetzgeber an vielen Stellen auch Vorgaben, z. B. in Form von ASIL (Automotive Safety Integrity Level)-Einstufungen in der Automobiltechnik. Sowie ein Hersteller Kenntnis über die Möglichkeit sicherheitskritischer Fehler erhält, ist er zum Rückruf verpflichtet. Dies ist bei Fahrzeugen einfacher durchführbar als bei gewöhnlichen Elektrogeräten – in jedem Fall aber mit hohen Kosten verbunden.
1.2.3 Frontloading
Interessant ist auch die Fehleranfälligkeit der einzelnen Entwicklungsphasen von Software (Abb. 1-4). Auch hier wurden statistische Erhebungen vorgenommen [3] mit durchaus interessantem Ergebnis.
Offensichtlich entstehen die meisten Fehler nicht, wie man sich vielleicht vorstellt, bei der klassischen Codeerstellung, sondern bereits in der Entwurfs- und Spezifikationsphase, also gleich zu Beginn des Projekts.
Abb. 1-4: Fehlerverteilung über die Softwareentwicklungsphasen
Besonders schwerwiegend ist hierbei, dass Fehler in der Spezifikationsphase oder gar im Lastenheft in den folgenden Absicherungsphasen, z. B. durch Funktionstests, nicht gefunden werden können, denn dieser Test erfolgt ja gerade gegen die Spezifikation!
Frontloading
Von großer Relevanz für die Fehlerbehebungskosten ist auch der Zeitpunkt im Entwicklungsprozess, zu dem ein Fehler gefunden wird (Abb. 1-5). Je früher dies gelingt, desto weniger kostenintensiv ist seine Beseitigung. Wenn ein Fehler erst sehr spät gefunden wird (im schlimmsten Fall erst am Ende des Lebenszyklus in der Wartung der Software), kann seine Behebung zweihundertmal so teuer sein, als wäre dies bereits in der Spezifikationsphase erfolgt.
Absicherungskapazitäten in
die frühen Phasen verlagern
Es ist also überaus sinnvoll, Absicherungskapazitäten bereits auf die frühen Entwicklungsphasen zu konzentrieren. Dieses Phänomen wird als Frontloading bezeichnet. Auf keinen Fall darf man also z. B. in der Spezifikationsphase die Qualitätssicherung außer Acht lassen. Fatalerweise ist allerdings oft genau dies zu beobachten. Eine Qualitätssicherung für Spezifikationen/Lastenhefteerfolgt oft allein dann, wenn sie Grundlagen für den Entwicklungsvertrag mit einem Zulieferer sind, wo nachträgliche Änderungen in der Regel kostenpflichtig sind.
Abb. 1-5: Frontloading und relative Fehlerbehebungskosten
1.2.4 Erfolgsfaktoren für den Test
In der Praxis haben sich einige Erfolgsfaktoren für Planung und Durchführung von Tests im Softwareentwicklungsprozess herausgebildet [2].
Tester so früh wie möglich in die
Entwicklung miteinbeziehen
Ganz wesentlich scheint die Beteiligung von Testern an der Prüfung von Anforderungen zu sein, z. B. durch Reviews. Letztlich erfolgt jeder Funktionstest auf Basis der und gegen die Spezifikation in Lastenheften. Spätestens hier fallen dem geübten Auge eines Testers nichttestbare oder lückenhafte Anforderungen auf. Frühzeitige Identifizierung und Behebung fehler- oder lückenhafter Anforderungen reduzieren das Risiko, dass falsche oder nichttestbare Funktionen entwickelt werden.
Auch die enge Zusammenarbeit von Systemdesignern und Testern in der Phase des Systementwurfs wirkt sich sehr vorteilhaft auf den Reifegradverlauf eines Softwareprojekts aus. Grundlegende Architekturfehler, die später kaum noch zu korrigieren sind, können so oft vermieden werden.
Arbeiten Entwickler und Tester in der Phase der Codeerstellung zusammen, hat das positive Auswirkungen auf das gegenseitige Verständnis und reduziert sowohl das Risiko von Defects im Quellcode als auch die Häufigkeit fehlerhafter Tests.
1.2.5 Die sieben Grundsätze des Softwaretests
Aus den Erfahrungen beim industriellen Softwaretest in den letzten Dekaden lassen sich wichtige Erkenntnisse ableiten, aber auch Trugschlüsse erkennen, was in die sieben Grundsätze des Softwaretests mündet:
Grundsatz 1: Testen zeigt (nur)
die Anwesenheit von Fehlern.
Die Durchführung von Tests reduziert die Wahrscheinlichkeit des Eintretens von Fehlerzuständen, weil diese dadurch frühzeitig erkannt und behoben werden können. Werden keine Fehlerzustände mehr identifiziert, bedeutet dies aber nicht die Fehlerfreiheit der Software. Testen kann nur die Anwesenheit von Fehlern zeigen, nie aber deren Abwesenheit!
Grundsatz 2: Es gibt keinen
erschöpfenden Test.
Ein vollständiger und alle Aspekte der zu testenden Software prüfender, erschöpfender Test ist außer bei trivialen Problemstellungen nicht möglich. Komplexität und Kombinatorik führen sehr schnell dazu, dass Testaufwand und Zeitbedarf in sehr unwirtschaftliche Regionen abdriften.
Grundsatz 3: Frontloading
spart Zeit und Geld.
Ein früher Testbeginn spart im Entwicklungsprojekt Zeit und Geld. Je früher ein Fehler identifiziert werden kann, desto schneller und kostengünstiger ist er zu beheben. Ein Fehler, der erst beim Kunden in Erscheinung tritt, kann ganz erhebliche Aufwände nach sich ziehen bis hin zum Rückruf des Produkts, sollte es sich um sicherheitskritische Mängel handeln.
Grundsatz 4: Fehler treten
in Clustern auf.
Es hat sich als durchgehendes Muster gezeigt, dass Fehler gehäuft auftreten (»wo einer ist, sind viele«). Aus bestimmten funktionalen, formalen oder projektspezifischen Gründen ist die Fehlerneigung nicht über den gesamten Quellcode gleich verteilt. Bei einer Aktualisierung der Ressourcenplanung ist es also sinnvoll, die bislang auffällig gewordenen Teilsysteme in ihrer Priorität heraufzustufen – dies ist auf jeden Fall ein besserer Ansatz als die »Gießkanne«.
Grundsatz 5: Auch Tests altern!
Die repetitive Durchführung immer derselben Testsuites stiftet auch beim Regressionstest immer weniger Nutzen. Mit denselben Tests werden natürlich dieselben Fehler gefunden, aber eben auch nur die. Die Tests verlieren sozusagen im Laufe der Zeit ihre Wirksamkeit. Mit Fortschreiten der Entwicklung ist es deshalb unerlässlich, den Testbestand zu überarbeiten und auf diese Weise bislang auch unberücksichtigte Codeanteile beim Test abzudecken.
Grundsatz 6: Einsatzgebiete
sind zu beachten.
Bei Testplanung und Testkonzept ist unbedingt auf die Berücksichtigung des späteren Einsatzgebiets der Software zu achten. Eine Bürosoftware ist anders zu testen als ein eingebettetes System. Dies kann bis zur Definition typischer Anwendungsfälle gehen, sog. operationaler Profile.
Grundsatz 7: Auch fehlerfreie Systeme
können völlig unbrauchbar sein.
Auch die völlige Abwesenheit von Fehlern – und wir sprechen hier nur von Tests gegen existierende Anforderungen – muss nicht zwangsläufig bedeuten, dass das getestete System einsatzfähig ist. Zum einen könnten existierende Anforderungen übersehen und nicht getestet worden sein. Ein Funktionstest prüft nur gegen im Test vorgesehene Anforderungen. Zum anderen wäre es denkbar, dass einige Anforderungen in der Spezifikation nicht berücksichtigt wurden und somit auch nicht bei der Implementierung der Software – folglich auch nicht beim Test. Die frühzeitige Einbeziehung der späteren Nutzer im Entwicklungsprozess und die Nutzung von Prototypen schafft hier Abhilfe!
1.2.6 Testaufwände
weitere Kostenaspekte
Testen ist eine aufwendige und teure Entwicklungsphase. Es lohnt sich also, etwas genauer hinzuschauen und diese Phase effizient und intelligent zu gestalten, um mit dem geringstmöglichen Aufwand den bestmöglichen Reifegrad zu erzielen. Aber wo genau entstehen diese Kosten in der Softwareentwicklung?
Größere Vorhaben mit über zehn Personenjahren Entwicklungsaufwand weisen ca. 50 % Integrations- und Testaufwände über die einzelnen Phasen auf, der Rest entfällt auf die Implementierung. Bei sicherheitskritischen Systemen können Integrations- und Testaufwände auf über 80 % ansteigen.
Abb. 1-6: Verteilung Softwareentwicklungskosten
Durch die hohen Aufwände ist beim Testen von Software stets auf Effizienz und Wirtschaftlichkeit zu achten. Testing ist das wichtigste und zugleich aufwendigste analytische Qualitätssicherungsverfahren. Ineffizienzen drohen dabei letztlich von zwei Seiten:
Testautomatisierung
Zum einen von hohem personellem Aufwand während der Testdurchführung. Hier ist die Lösung die Einführung einer durchgängigen Testautomatisierung. Tests sollten ohne menschliche Interaktion 24 / 7 automatisiert durchgeführt und ausgewertet werden.
Testmethodik
Zum anderen von geringen Fehleraufdeckungsraten. Hier heißt die Lösung: Einführung einer intelligenten Testmethodik. Weg von stupidem, repetitivem »viel hilft viel« hin zu smarten, ans Problem adaptierten Testabläufen. Das Wie macht die Musik!
Bei größeren Softwareprojekten sind
methodisches und automatisiertes
Testen Pflicht.
Beide Aspekte, Testautomatisierung und Testmethodik, bilden einen wesentlichen Schwerpunkt in diesem Buch. Optimierung an dieser Stelle gleicht oftmals einem Transistoreffekt: Vergleichsweise geringe Eingriffe in den Absicherungsprozess können eine hohe Kostenwirksamkeit aufweisen.
Die Erfahrung zeigt, dass Testautomatisierung und Testmethodik mit geringeren Aufwänden reproduzierbar und prädizierbar bessere Reifegrade in der Softwareentwicklung ermöglichen.
1.3 Analytische Qualitätssicherung
QS-Maßnahmen im Überblick
Die Qualitätssicherung (QS) eines Unternehmens befasst sich mit der Einhaltung und Anwendung von dokumentierten Arbeitsprozessen. Diese haben zum Inhalt, vereinbarte Qualitätsmerkmale, und somit das Qualitätsniveau entwickelter Produkte, sicherzustellen. Grundsätzlich unterscheidet man drei Gruppen von QS-Maßnahmen:
▶Datenmanagement-Methoden: Hierbei geht es um die Einführung von Abläufen wie Softwareentwicklungsprozessen oder Vorgehensmodellen. Auch Dokumenten-/Konfigurationsmanagement und Versionsverwaltung fallen in diese Kategorie wie auch z. B. die Zertifizierung von Entwicklungswerkzeugen.
▶Konstruktive Methoden und Maßnahmen: Hierbei hat man es mit konkreten Maßnahmen für den Entwicklungsvorgang zu tun wie z. B. Anforderungsmanagement (Requirements Engineering und Management). Auch Methoden und Werkzeuge für Spezifikation, Modellierung und Entwurf fallen hierunter, zudem die Etablierung von Richtlinien wie z. B. Codierrichtlinien oder die Verabschiedung von Konventionen und Standards.
▶Analytische Methoden und Maßnahmen: Diese Kategorie umfasst alle Testverfahren, somit auch z. B. statische Analysen, die wir noch kennen lernen werden. Analytische Methoden bilden einen weiteren Schwerpunkt dieses Buchs.
Abb. 1-7 zeigt die in ISO 9126 definierten Qualitätsmerkmale für Software. Neben den offensichtlichen Qualitätsmerkmalen wie Funktionalität haben auch Änderbarkeit/Wartbarkeit und Zuverlässigkeit eine große Bedeutung. Software, die funktional einwandfrei arbeitet, aber beispielsweise (zurecht existierende) Codierrichtlinien missachtet oder unzureichend dokumentiert wurde, ist automatisch von minderer Qualität. Sie kann im weiteren Lebenszyklus kaum weiterentwickelt werden und Fehlersuche und -Behebung sind vergleichsweise aufwendig. Man spricht hier auch von interner und externer Qualität (Verhalten nach außen).
Abb. 1-7: Qualitätsmerkmale von Software nach ISO 9126
Qualitätsmanagement (QM)
Die Qualitätssicherung ist eingegliedert in das Qualitätsmanagement (QM) eines Unternehmens. Hierunter sind alle organisatorischen Maßnahmen zur Steuerung und Lenkung eines Unternehmens im Hinblick auf die Qualität seiner Produkte zu verstehen. Dies beinhaltet neben der Festlegung der Qualitätsziele auch die Qualitätsplanung sowie alle Maßnahmen der Qualitätssicherung und Qualitätsverbesserung. In Branchen wie der Automobilindustrie oder Raumfahrt ist die Umsetzung eines QM aufgrund potenzieller Gefährdungen von Menschen und Gütern sogar gesetzlich vorgeschrieben. Sehr verbreitet ist die Normenreihe ISO 9000 Quality Management Standards [4] mit dem Standard ISO / IEC 90003 Software Engineering [5], der die Anwendung der Richtlinien der ISO 9001 [6] auf den Softwarebereich festlegt.
Die analytische Qualitätssicherung umfasst eine ganze Reihe von Techniken zur Analyse der internen und externen Qualität von Software.
statische und dynamische
QS-Maßnahmen
Unterschieden wird zwischen statischen und dynamischen Techniken, wobei statische Techniken im Gegensatz zu den dynamischen für die Analyse keine Ausführung des SUT erfordern. Der klassische Funktionstest ist also eine dynamische Technik, die klassische Codeanalyse ein statischer Vertreter. Darüber hinaus gibt es je nach Ausrichtung, Testziel und Analyseobjekt noch eine Menge weiterer Techniken, die unten dargestellt werden. Im weiteren Verlauf dieses Kapitels werden die wichtigsten Techniken kurz vorgestellt, auf einige davon wird in Kap. 4 »Statischer Test« und Kap. 5 »Dynamischer Test« noch detailliert eingegangen.
Abb. 1-8: Übersicht Analytische Qualitätssicherungsmaßnahmen
1.3.1 Reviews
Ein wichtiger Vertreter statischer Analysetechniken sind Reviews. Hierbei handelt es sich um strukturierte Gruppenprüfungen, d. h., eine Gruppe mit der Analyse beauftragter Personen begutachtet verschiedene Entwicklungsdokumente, allen voran natürlich Quellcode und Spezifikationen, aber auch Vertrag, Testdokumente u. a. können Analysegegenstand sein.
Reviews gehören zu den
effektivsten QS-Maßnahmen.
In der Regel sind Reviews die einzige Möglichkeit, die Semantik von Dokumenten (»ergibt das eigentlich Sinn?«) zu überprüfen. Kein Test- oder Codeanalysewerkzeug wird je eine Antwort auf diese Frage geben können. Aber gerade solche »sinnlosen« Fehler in den frühen Entstehungsphasen von Software sind später nur mit erheblichem Aufwand zu beseitigen.
Bei Reviews wird der Wissensaustausch zwischen den Beteiligten gefördert, und zwar sowohl im Hinblick auf das Projekt (einheitliches Verständnis) als auch im Sinne der fachlichen Qualifikation.
Reviews weisen eine Fehlerfin-
dungsrate von bis zu 70% auf!
Untersuchungen zeigen, dass durch gut geführte Reviews bis zu 70 % der in Dokumenten enthaltenen Fehler gefunden werden können – dies ist einer der höchsten Werte in der analytischen Qualitätssicherung überhaupt.
Abb. 1-9: Reviews gemäß Vorgehensweise IEEE 1028
1.3.2 Statische Analysen
Prüfung des Programmcodes
auf Erfüllung von Kriterien,
ohne ihn auszuführen
Statische Analyse ist die Untersuchung des statischen Aufbaus eines Programmcodes auf die Erfüllung vorgegebener Kriterien hin. Die Prüfung ist statisch, d. h., das Testobjekt wird nicht ausgeführt. Statische Analysen grenzen sich damit vom dynamischen Test ab. Sie sind formal durchführbar und damit automatisierbar, womit sie sich vom Review abgrenzen. Als wichtigste Ziele der statischen Analyseverfahren sind zu nennen:
▶Bewertung von Qualitätsmerkmalen, z. B. Pflegbarkeit, Testbarkeit aufgrund struktureller Eigenschaften des Prüflings (z. B. Komplexität, Anomalien),
▶Erkennen von Fehlern und Anomalien in neu erstellter Software,
▶Erkennen und Analyse von Programmstrukturen bei der Pflege und beim Reengineering von Altsystemen,
▶Prüfung, ob ein Programm formale Vorgaben (z. B. Codierrichtlinien, Namenskonventionen, etc.) einhält.
Statischen Analysen ist aufgrund ihrer großen Bedeutung bei der Qualitätssicherung von Software in diesem Buch ein eigenes Kapitel gewidmet.
In der Regel werden für die Durchführung statischer Analysen leistungsfähige Werkzeuge benötigt. Auch auf diese wird im weiteren Verlauf dieses Buchs eingegangen.
1.3.3 Symbolische Ausführung
Bei der symbolischen Ausführung wird die Programmausführung mit symbolischen Werten für Eingaben und Zustände simuliert. Das Programm wird Zeile für Zeile abgearbeitet, ohne Werte für die Eingangsvariablen anzugeben.
Analyseverfahren sollten ohne
Expertenwissen anwendbar sein,
sonst sinkt die Akzeptanz.
Auf jedem Programmpfad wird also für jede Zeile ein symbolischer Ausdruck für Ausgaben und (neue) Zustände berechnet. Die Überprüfung der Ergebnisse ist dadurch schwieriger als beim konventionellen Test, da symbolische Ausdrücke und keine Zahlenwerte mit der Spezifikation verglichen werden müssen. Dies ist automatisiert kaum darstellbar. Es wird zudem ein Theorembeweiser benötigt, der alle erforderlichen Umformungen berechnen kann, z. B. a + 2 . b – a = 2 . b.
Diese Einschränkungen führen dazu, dass die symbolische Ausführung außerhalb des wissenschaftlichen Umfelds im betrachteten industriellen Kontext keine wesentliche Rolle spielt.
1.3.4 Model Checking
Model Checking analysiert das Verhalten des SUT nicht auf Ebene des Sourcecodes, sondern des äquivalenten Funktionsmodells (z. B. einem Matlab Simulink-Modell). Oft wird eine Funktion nicht mehr von Hand programmiert, sondern in Form eines Modells beschrieben.
Modelle bieten als formale Be-
schreibungsmittel einige Vorteile.
Es handelt sich hier aber nur um eine andere Art der Darstellung. Dies ist vorteilhaft, weil Menschen komplexe Zusammenhänge oder Funktionsbeschreibungen in grafischen Notationen besser erfassen und verstehen können als durch das Studium langer Texte. Zudem können Modelle simulativ analysiert werden – eine Analyseebene, die bei reinem Quellcode nicht zur Verfügung steht. Ausgehend von diesem Funktionsmodell erfolgt dann nach Analyse durch Simulation über eine automatische Codegenerierung die Erstellung eines entsprechenden, funktionsäquivalenten Quellcodes.
Model Checking nutzt die Modellebene aber noch über die reine Simulation hinaus. Es ermöglicht, das Systemmodell auf die Erfüllung spezifischer Eigenschaften abzuprüfen.
Beispiel: »Feindliches Grün«
Bei einer Ampelsteuerung, die als Funktionsmodell vorliegt, wäre z. B. die Eigenschaft von besonderem Interesse, ob es im Betrieb – auf welche Weise auch immer – möglich ist, dass Ampeln kreuzender Fahrspuren gleichzeitig Grün anzeigen. Dies bezeichnet man auch als »feindliches Grün«. Auch wenn dies in der Spezifikation sicher nicht vorgesehen ist, wäre ja denkbar, dass ein Implementierungsfehler im Modell dies dennoch zulassen könnte.
Wir fragen nach konkreten
Eigenschaften des Systems.
Model Checking ermöglicht die Überprüfung spezifischer Eigenschaften, z. B. ob es grundsätzlich möglich ist, dass dieses Modell ein »feindliches Grün« zulässt. Ist diese Eigenschaft erfüllt? Nun laufen komplexe Algorithmen ab und das Verfahren liefert entweder die Antwort »Nein« oder es liefert ein konkretes Beispiel, in dem diese Eigenschaft erfüllt ist.
Dies ist eine besondere Art der funktionalen Analyse. Im Gegensatz zum klassischen Funktionstest, der im Prinzip nur auf vielfachem Probieren basiert, könnte man nun sicher sein, dass auch bei beliebig vielen Tests das abgefragte Fehlverhalten nie auftreten würde. Beim Testen allein könnte man nie sicher sein, dass nach vielen fehlerfreien Tests und erreichtem Testende nicht doch der nächste Test ein »feindliches Grün« aufgezeigt hätte. Im weiteren Verlauf dieses Buch werden uns noch weitere Vertreter dieser Art von geschlossenen Fragestellungen begegnen.
Die Möglichkeit, Eigenschaften eines SUT abzufragen (»Ist es prinzipiell möglich, dass …?«), bietet als wertvolle Ergänzung erhebliche Vorteile gegenüber dem klassischen Funktionstest.
Allerdings hat diese Vorgehensweise auch ihren Preis. Zum einen wird in der Tat auch nur exakt diese eine Frage beantwortet – alle anderen Funktionsaspekte bleiben ungetestet –, andererseits kann Model Checking mit erheblichen Rechenzeiten einhergehen. Letztlich laufen komplexe Algorithmen über den Modellgraphen, und um Rechenperformance zu optimieren, können Einschränkungen an den Beschreibungsmerkmalen im Funktionsmodell vorgenommen werden. Ob diese Einschränkungen vertretbar sind, muss von Fall zu Fall entschieden werden.
Besonders sicherheitskritische Modellteile werden oft durch Model Checking analysiert. Fest steht, dass lange Analysezeiten oft zu Lasten der industriellen Akzeptanz gehen. In der automobilen Serienentwicklung wird Model Checking in der Regel nur bei spezifischen Fragestellungen im sicherheitskritischen Bereich eingesetzt, wo der Nachweis der Erfüllung konkreter Modell- und Systemeigenschaften einen entscheidenden Vorteil gegenüber dem stichprobenhaften Vorgehen beim Testen darstellt. Die zu analysierende Modell- und Systemeigenschaft wird verifiziert oder falsifiziert.
1.3.5 Simulation
Software nicht von Hand
schreiben, sondern aus einem
Modell heraus generieren
Bei simulativen Analyseverfahren erfolgt eine dynamische Ausführung des SUT als Modell auf emulierter Komponentenhardware, wenn z. B. die Zielhardware noch nicht verfügbar ist. Wie bereits aufgezeigt, bietet das verhaltensäquivalente Funktionsmodell bessere analytische Möglichkeiten als der klassische Test des Quellcodes. Eine solche analytische Möglichkeit ist die Simulation und es steht eine Vielzahl kommerzieller wie nichtkommerzieller Werkzeuge zur Verfügung mit einer Fülle an Analysemöglichkeiten.
In der Automobilindustrie kommt oftmals Matlab Simulink zum Einsatz. Vor dem Test des Codes direkt auf der Zielhardware, dem finalen Steuergerät, kann man hier bereits frühzeitig mit einem handelsüblichen PC Funktions- bzw. algorithmische Fehler durch Simulation eliminieren.
Abb. 1-10: Hierarchisches Funktionsmodell in Simulink
Grafische Notationen machen Funktionen
besser verständlich als lange Listings.
Die Simulation ist eines der am weitesten verbreiteten Analyseverfahren. Sie ist vergleichsweise kostengünstig, intuitiv anwendbar und bietet viele Möglichkeiten. Durch die automatische Codegenerierung aus dem Modell (zielgerichtet optimiert für spezifische Ziel-Controller) muss die Funktion nur einmal modelliert werden, die klassische und fehleranfällige manuelle Codeerstellung kann entfallen. Wer allerdings glaubt, automatisch generierter Code sei fehlerfrei, der sei hier eines Besseren belehrt, wie Codeanalysewerkzeuge uns Tag für Tag aufzeigen. Doch auch dazu später mehr.
Simulation prüft nicht das SUT,
sondern nur dessen Abstraktion.
Eines sollte man bei der Simulation im Auge behalten. Simulation ist immer eine Abstraktion von der Realität. Je realitätsnäher das Modell ist, desto geringer ist dieser Unterschied. Er ist aber immer existent.
Simulation ist immer eine Abstraktion von der Realität. Es hängt vom Testziel ab, ob der Unterschied zwischen Modell und Realität tolerierbar ist. Keine Simulation kann den finalen Test des realen Systems ersetzen.
Modellbildung kann für komplexe Systeme extrem aufwendig sein. Diesen Aufwand muss man treiben, wenn aus Simulationsergebnissen belastbare Erkenntnisse gezogen werden soll. So kann es sein, dass aufgrund zu großer Abstraktion des Modells oder schlichtweg aufgrund von Modellfehlern das Modell sich so weit vom eigentlichen SUT unterscheidet, dass eine fehlerfreie Simulation nicht automatisch ein fehlerfreies SUT bedeutet. Und umgekehrt spiegeln sich womöglich simulativ gefundene Fehler gar nicht im realen SUT. Wir dürfen nicht vergessen: Testziel ist die Fehlerfreiheit des Codes auf dem Steuergerät und nicht nur die des Modells!
industrielle Nutzung
Im industriellen Kontext werden daher erhebliche Aufwände getrieben, um mit qualitativ höchstwertigen Modellen arbeiten zu können. Nur so kann man aufgrund der Verlässlichkeit der Ergebnisse durch die simulative Erprobung die teurere Erprobung auf der Zielhardware bzw. in der Zielumgebung reduzieren. In der Automobilindustrie kommen nicht selten bei HiL-Simulatoren Fahrzeugmodelle zum Einsatz, für die Dutzende von Personenjahren angesetzt werden können.
Wie komplex sollte ein Modell sein?
Und ein weiterer Aspekt ist nicht zu vernachlässigen: Je komplexer und »besser« ein Modell ist, desto rechenaufwendiger ist die Simulation. Wenn die Ausführung eines Codes bis auf die Simulation der Prozessorhardware reicht, liegt ggf. ein Vielfaches zwischen Simulationszeit und Echtzeit, d. h. es könnte Minuten dauern, um eine Sekunde eines Funktionscodes zu simulieren. Andererseits ist es oftmals das Ziel, dass die Simulation so performant ist, dass Echtzeit erreicht werden kann. Hintergrund ist der Wunsch, bei der Absicherung eines Systems reale und simulierte Komponenten zu mischen, d. h., beide Welten benötigen dieselbe Zeitbasis, um miteinander kommunizieren zu können. Es werden also sehr leistungsfähige Hardwareplattformen zum Einsatz gebracht.
Ziel ist es, die Echtzeit zu schlagen.
Bei der Wahl der Modelltiefe sollte man allerdings aus wirtschaftlichen Gründen auch nicht über das Ziel hinausschießen. Es gilt der Grundsatz: So viel wie nötig, so wenig wie möglich. Die Systeme werden also so dimensioniert, dass noch genügend Rechenreserven vorhanden sind, um in jeder Situation noch Performance in Echtzeit erreichen zu können. Und ist die Simulation schneller als die Realität, ist das kein Problem: Die Realität kann zwar nicht auf die Simulation warten (die Dauer einer Sekunde lässt sich leider nicht ändern, auch wenn man das Gefühl hat, dass die Zeit manchmal viel zu schnell vergeht …), umgekehrt ist das aber sehr wohl möglich: Rechner sind sehr geduldige Wesen.
1.3.6 Testmethoden
Intuition und Bauchgefühl sind
keine guten Berater beim Test.
Wie bereits ausgeführt, liegt der Schlüssel zur Effizienz beim Testen in der Automatisierung und Methodik der Tests. Ein ziel- und planloses Ausprobieren verschiedener Stimuli »nach Intuition« und das Beenden des Testprozesses »nach Bauchgefühl« sind der beste Garant für eine kostenintensive und dennoch in puncto Reifegrad schlechte Absicherung.
Die Kunst des Testens besteht darin, mit möglichst wenigen Tests die wesentlichen Fehler zu finden.
Worauf kommt es beim Testen an?
Das Testergebnis ist immer auch im wirtschaftlichen Kontext zu sehen. Niemand kann es sich leisten, für einfache Steuerungen Monate an Testaufwand zu investieren. In kurzer Zeit ein akzeptables Ergebnis zu erreichen, ist oftmals besser, als sich über einen langen Zeitraum an das perfekte Ergebnis anzunähern. Zeit ist im industriellen Kontext ein rares Gut, und wenn die verbleibenden Fehler tolerierbar sind, ist dies der optimale Weg. Es lohnt sich für viele Systeme nicht, die letzten 5 % vorhandener Fehler aus dem Code zu eliminieren, wenn man dafür 50 % der bislang investierten Ressourcen bräuchte! Man sollte sich allerdings Gedanken über die kritischsten Fehler gemacht haben und diese absichern. Mit den dann noch verbleibenden Auffälligkeiten kann man ggf. leben und diese erst bei Auftreten beseitigen.
A fool with a tool is still a fool.
Testmethodik kann als das Know-how des Testens bezeichnet werden. Die zum Einsatz kommende Testtechnologie ist nur eine notwendige, aber keine hinreichende Bedingung für den effizienten Testerfolg – ich erinnere an den Wahlspruch der 1990er Jahre: »A fool with a tool is still a fool.«, als man in tiefer Toolgläubigkeit in Werkzeuge investierte, ohne sie richtig verstanden zu haben oder anwenden zu können. Merke: Die beste Technologie nutzt nichts, wenn sie nicht richtig angewandt wird!
Testen muss planvoll erfolgen mit
klar definiertem Ziel und Ende.
In diesem Kontext ist der Einsatz von Testmethodik zu sehen. Wenn ein Testingenieur nicht sofort auf die Frage antworten kann, warum gerade jetzt genau dieser Test ausgeführt wird, verfolgt er höchstwahrscheinlich keinen Plan und seinen Tests liegt womöglich keinerlei Methodik zugrunde.
Im Folgenden soll ein Überblick über die verbreitetsten Testmethoden gegeben werden. Diese werden in Form von Steckbriefen mit den wichtigsten Charakteristika und einer Bewertung vorgestellt, um in diesem Kapitel einen schnellen Überblick zu vermitteln. Eine Vertiefung erfolgt dann noch in den folgenden Kapiteln, insbesondere in Kap. 5 (Dynamischer Test).
1.3.6.1 Funktionaler Test
Funktionstest erfolgt
gegen die Spezifikation.
Der wichtigste Vertreter im Bereich Testmethodik ist der funktionale Test oder auch Funktionstest mit einer Reihe von Untermethoden. Allen gemein ist das Prinzip, dass ein Funktionstest immer gegen die Spezifikation erfolgt. Man liest die Spezifikation des SUT und leitet daraus die durchzuführenden Testinhalte ab.
Eigenschaften von Lastenheften
Technische Spezifikationen z. B. für elektronische Steuergeräte umfassen eine Vielzahl von Anforderungen, Diagrammen und Beschreibungen technischer Abläufe des Sollverhaltens. Für die Erfassung haben sich in den letzten Jahren Werkzeuge wie z. B. DOORS als Standard etabliert. Sie bringen Struktur in die große Menge an funktionalen Teilbeschreibungen, indem sie Anforderungen (Requirements) vereinzeln, d. h. einzeln referenzierbar machen. Jede Anforderung erhält eine ID und kann im Übrigen auch zu Testdaten oder anderen Dokumenten verlinkt werden.
Unter Testabdeckung versteht man den Anteil aller Anforderungen, die bereits in einem Test überprüft wurden. Man könnte z. B. für jede Anforderung einen Test erstellen, der den in der Anforderung dokumentierten technischen Sachverhalt prüft.
kein klares Testende
Ein Problem beim Funktionstest ist allerdings, dass es keinen klar erkennbaren oder definierten Testumfang gibt, den es zu bearbeiten gilt. Genügt es, jede Anforderung einmal abzusichern? Oder ist das womöglich gar nicht erforderlich? Muss man Kombinationen von Anforderungen bilden und gemeinsam, d. h. in Verbindung miteinander testen? Auf all diese Fragen gibt es keine klaren Antworten.
keine Automatisierung
Weiterhin sind Funktionstests nur schwer automatisierbar. Idealerweise kann das Testsystem (das den Test ausführende System – in Abgrenzung zum SUT) auch automatisch die korrekte, erwartete Antwort ermitteln, um die Testbewertung durchführen zu können (Pass/Fail). Man versuche dies mit einem in Prosa geschriebenen Lastenheft!
Abb. 1-11: Steckbrief funktionaler Test
1.3.6.2 Strukturtest
Während der funktionale Test gegen die Spezifikation einzelne Anforderungen referenziert (Testziel sind die Anforderungen), nutzt der Strukturtest die Strukturelemente als Testziele. Als Strukturelemente kommen z. B. Anweisungen, Zweige, Bedingungen oder Pfade des Programmcodes infrage.
Testziel sind die Strukturelemente
im Programmcode.
Kann es beim Funktionstest Testziel sein, alle Anforderungen abzutesten, wäre beim Strukturtest ein Testziel denkbar, Programmzweige oder Bedingungen mindestens einmal zu durchlaufen. Eine vollständige Testabdeckung wäre z. B. erreicht, wenn das Programm durch die Tests jede Bedingung mindestens einmal durchlaufen hätte. Die Tests müssen nun so gewählt bzw. das zu testende Programm so stimuliert werden, dass dieses Ziel erreicht wird. In der Regel kann dies nicht in nur einem Testlauf geschehen und es sind mehrere Testläufe erforderlich.
Die Entwicklung der Variablenwerte im
Programmablauf steht im Vordergrund.
Bei datenflussorientierten Testverfahren liegen den Testzielen nicht die Strukturelemente des Programms, sondern Zugriffe auf Variablen bzw. deren »Werdegang« im Ablauf eines Programms zugrunde. Doch hierzu später mehr.
Bei der Bewertung des Strukturtests bleibt festzustellen, dass es sich um ein sehr verbreitetes Testverfahren handelt. Die Werkzeugunterstützung ist vielfältig und die Testautomatisierbarkeit sehr hoch. Hat das Testwerkzeug die Quellen erst einmal eingelesen und analysiert, ist es ein Leichtes, die für bestimmte Programmläufe des SUT erforderlichen Stimuli zu berechnen, das SUT entsprechend mit diesen Stimuli aufzurufen, das Ergebnis auszulesen und mit der richtigen Sollantwort zu vergleichen. Alles vollautomatisch!
Anwender erleben die Funktion
von Software, nicht das Durch-
laufen ihrer Strukturelemente.
Man sollte nur nicht vergessen, dass man sich hier auf einer rein formalen Ebene bewegt. Anwender hingegen erleben Funktionen, nicht das Durchlaufen eines Strukturelements. Man kann also mithilfe des Strukturtests nie Aussagen treffen, ob wirklich alle im Lastenheft geforderten Funktionen auch fehlerfrei umgesetzt sind. Man kann noch nicht einmal beurteilen, ob sie überhaupt implementiert sind. Strukturtests können also nicht die ganze Wahrheit, sondern nur Bausteine (wenn auch wichtige Bausteine) in der Absicherungskette sein, die mit anderen Elementen sinnvoll kombiniert werden müssen.
Eine vollständige Abdeckung ist beim
Strukturtest nicht immer möglich.
Aufgrund der individuellen, strukturellen Eigenschaften von Programmcode ist es je nach der gewählten Metrik nicht möglich, eine vollständige Überdeckung zu erreichen. Jeder Test entspricht hierbei einem konkreten, ggf. für die vollständige Abdeckung noch fehlenden, Programmverlauf und muss durch geschickte Wahl der Programmeingangsdaten vom Testsystem herbeigeführt werden. Dies ist allerdings nicht immer möglich, da sich unter Umständen bestimmte Wertekombinationen widersprechen, d. h. von der Spezifikation ausgeschlossen wurden.
Abb. 1-12: Steckbrief Strukturtest
1.3.6.3 Statistischer Test
operationales Profil
Beim statistischen Test erfolgt die Testdatengenerierung nicht auf Basis struktureller Eigenschaften des Codes, sondern zufällig gesteuert. Die Testdaten werden oft einem operationalen Profil entsprechend generiert, also einem typischen Anwendungsfall für das spätere Produkt. Bei mehreren Anwendungsfällen werden nach der Häufigkeit der beim späteren Betrieb zu erwartenden Programmabläufe Testdaten generiert. Da operationale Benutzungsprofile durch eine in der Regel einfache Schnittstelle hier leicht zu ermitteln sind, finden statistische Testverfahren oft Anwendung bei der Absicherung von Telekommunikationssystemen, also z. B. der Nutzung von Endprodukten oder Übertragungssystemen.
Die Testdatengenerierung
orientiert sich an häufigen
Nutzungsarten der Software.
Diese Testmethodik stellt sicher, dass sehr schnell Fehler hoher Kundenrelevanz gefunden werden. Situationen, in denen Nutzer bereits nach kurzer Zeit schwerwiegende Fehler in einer neuen Software entdecken, sollten dann sehr unwahrscheinlich sein.
Außergewöhnliche Verwendungen des Produkts werden andererseits entsprechend schlechter abgesichert. Dies ist bei knapper Testzeit sicher die bessere Vorgehensweise, als die häufigen Anwendungsfälle unzureichend abzusichern. Andererseits können auch seltene Nutzungsszenarien von besonderer Bedeutung sein oder zu großen Schäden führen. Aus diesen Gründen kommt dem statistischen Test bei der Entwicklung eingebetteter Systeme nur Nischenbedeutung zu, es wird eine zielgerichtetere Testdatengenerierung bevorzugt. Oft wird das Testverfahren mit Verfahren der systematischen Testfallableitung kombiniert, um die Vorteile beider Verfahren zu vereinen. Durch die einfache Testdatenermittlung und die hohe Automatisierungsmöglichkeit der Testdurchführung ist der zusätzlich entstehende Aufwand vergleichsweise gering. Gezielt bestimmte Testszenarien oder Parameterkombinationen abzusichern ist hingegen nicht darstellbar.
keine Automatisierung möglich
Zudem ist die Ermittlung eines zuverlässigen operationales Profils bei eingebetteten Systemen oftmals schwierig und das Vorgehen ist auch kaum zu automatisieren. Wie sollte das Testsystem ermitteln können, was das richtige Ergebnis einer zufälligen Stimulation des SUT gewesen wäre bzw. ist?
Testorakel ermitteln
richtige Testergebnisse.
Programme, die zu einem bestimmten Stimulus des SUT die korrekte, spezifikationskonforme Antwort ermitteln, existieren aber durchaus. Man nennt sie vielsagend Testorakel.
Abb. 1-13: Steckbrief statistischer Test
1.3.6.4 Mutationentest
ein Testverfahren für Testverfahren
Beim Mutationentest handelt es sich um kein Verfahren zur Gewinnung von Testfällen im eigentlichen Sinne, sondern um eine Beurteilung der Güte von Testmethoden bzw. Testfallableitungsalgorithmen.
Das Testsystem ändert/mutiert geringfügig ein SUT und überprüft, ob diese gezielt eingebrachten Fehler, die ja Abweichungen vom spezifizierten Verhalten darstellen, bei Anwendung der Testfallableitungsmethodik gefunden werden.
Der Automatisierungsgrad ist hoch, in der Praxis hat dieses Verfahren bei der Entwicklung eingebetteter Systeme allerdings keine besondere Relevanz, wohl aber bei der Bewertung der hierfür eingesetzten Testableitungsverfahren.
Abb. 1-14: Steckbrief Mutationentest
1.3.6.5 Evolutionärer Test
Ein Traum wird wahr.
Ein besonders interessanter und eher forschungsorientierter testmethodischer Ansatz ist die evolutionäre Testfallableitung. In der Praxis hinter dem funktionalen Test zurückbleibend, wird ein Verfahren zur vollautomatischen Testfallgenerierung verfolgt – dem Traum jedes Testverantwortlichen.
vollautomatische Testgenerierung für
Funktions-, Struktur- und Realzeittest
Den evolutionären Testverfahren ist im weiteren Verlauf dieses Buchs ein eigenes Kapitel gewidmet, deshalb sei hier nur festgehalten, dass sich diese Methodik der Testfallableitung für verschiedene Anwendungen von Funktionstest über Strukturtest bis hin zum Realzeittest (z. B. Ermittlung der längsten Ausführungszeit, Worst Case Execution Time) eignet. Genetischen Algorithmen folgend werden zielgerichtet Stimuli iterativ so lange immer weiterentwickelt und (im Hinblick auf die Eigenschaft, ein Fehlverhalten hervorzurufen) optimiert, bis ein Fehler auftritt, das SUT quasi »gebrochen« wurde. Gelingt die Herbeiführung eines Fehlverhaltens nicht in einer gewissen Zeit, weist das System den Fehler mit hoher Wahrscheinlichkeit auch nicht auf und das Verfahren bricht ab.
Evolutionäre Verfahren zeichnen sich dadurch aus, dass man nur sehr wenig über das SUT wissen muss (Black Box) und eine Eignung für große Parameterräume bei vollständiger Automatisierbarkeit vorliegt.
Abb. 1-15: Steckbrief evolutionärer Test
1.3.7 Back-to-Back-Test und Testorakel
Beim Einsatz von automatisierten Methoden zur Testfallableitung ist es natürlich erforderlich, zu den erlangten Testfällen auch die Ermittlung der spezifikationskonformen (also korrekten) Antworten sowie auch die Testbewertung automatisieren zu können.
Wie funktioniert ein Testorakel?
Systeme zur Ermittlung spezifikationskonformer Antworten bezeichnet man als Testorakel. Beim Soll-/Istwert-Vergleich unterstützen sie bei der Feststellung der Sollwerte. Dies ist nicht in allen Fällen möglich, es wird z. B. nicht gelingen, aus einem Lastenheft diese Informationen automatisiert zu extrahieren. Je nachdem, in welcher Weise die Spezifikation vorliegt, ergeben sich allerdings gewisse Möglichkeiten der Automatisierung.
Ein Testorakel kann z. B. ein ausführbarer Prototyp eines Lastenhefts sein. In der Tat werden oftmals statt eines textuellen Lastenhefts ausführbare Modelle vom Auftraggeber an den Zulieferer übergeben, die exemplarisch die wesentlichen Aspekte des zu implementierenden Verhaltens aufweisen – allerdings nur als Hülle und ohne reale Implementierung. Auch auf Robustheitsmaßnahmen wird hier weitgehend verzichtet. Liegt ein derartiger ausführbarer Prototyp vor, kann er als Testorakel genutzt werden.
Back-to-Back-Tests arbeiten
nur scheinbar redundant ...
Ein weiteres Einsatzgebiet für Testorakel ist der Back-to-Back-Test. Das zu testende Programm wird zweimal parallel und unabhängig voneinander erstellt. Jede Version wird mit denselben Testdaten beaufschlagt. Bei unterschiedlichen Ergebnissen ist eine Version fehlerhaft. Nur diejenigen Fehlerzustände, die in beiden Versionen mit selber Fehlerwirkung vorhanden sind, bleiben unentdeckt.
... und sind vor allem voll-
ständig automatisierbar!
Der Vorteil eines Back-to-Back-Tests ist die Automatisierbarkeit. Es ist ein Leichtes, beide Programmversionen mit denselben Testdaten zu beaufschlagen und die Antworten zu vergleichen.
Warum, so fragen Sie, sollte aber jemand auf die Idee kommen, dasselbe Programm zweimal zu entwickeln? Die Antwort lautet: Wenn Aufwände eine untergeordnete Rolle spielen, z. B. bei hochsicherheitskritischen Systemen. Die Wahrscheinlichkeit, dass dieselben Fehler bei unabhängigen Entwicklungen unterlaufen, ist vergleichsweise gering. Werden diese Programmversionen im Betrieb eingesetzt, spricht man von heterogener Redundanz. Dies ist z. B. bei Flugsteuerungen der Fall. Mehrere Einheiten (z. B. drei) bearbeiten dieselbe sicherheitskritische Aufgabe, und wenn alle Systeme zum selben Ergebnis führen (ein einfacher Vergleich genügt), wird das Ergebnis akzeptiert. Falls ein System abweicht, nimmt man das Ergebnis der beiden anderen übereinstimmenden.
homogene/heterogene Redundanz
Während sich die heterogene Redundanz als robust gegen Entwurfs- und Implementierungsfehler erweist, zeigt sich die homogene Redundanz (exakt das gleiche System kommt mehrfach zum Einsatz) nur robust gegen den Ausfall einer Einheit (z. B. Hardwaredefekt). Auch beim autonomen Fahren wird sicheres Verhalten durch Redundanz erkauft.
1.4 Psychologische Aspekte des Testens
Ein Sprichwort sagt »Irren ist menschlich«, und so ist es nicht verwunderlich, dass Entwicklern bei einer so anspruchsvollen Aufgabe wie der Softwareerstellung Fehler unterlaufen. Entscheidend ist die Art und Weise, wie Fehler im Unternehmen kommuniziert werden, sei es nun die Mitteilung eines Reifegrads an das Management oder die Information eines betroffenen Entwicklers durch die Testergruppe.
Fingerspitzengefühl und fakten-
orientierte Dokumentation
Die Art und Weise, wie dies erfolgt, kann für die Zusammenarbeit der betroffenen Personengruppen positive oder negative Auswirkungen haben. Es ist immer ein besonderes Fingerspitzengefühl gefragt, um zu vermeiden, dass die Kommunikation von Fehlerzuständen nicht als Kritik am Produkt und seinem Autor aufgefasst wird. Die Testergruppe darf auch nicht als Überbringer schlechter Botschaften für deren Inhalte verantwortlich gemacht werden. Gewisse soziale Kompetenzen helfen, dieses Konfliktpotenzial deutlich zu senken und den respektvollen Umgang miteinander aufrechtzuerhalten. Laute Töne oder gar Streit sind stets zu vermeiden, da sie nicht förderlich sind für eine gute Zusammenarbeit zwischen Entwicklern und Testern.
das Gute im Fehler sehen!
Erkannte Fehlerwirkungen sind positiv aufzufassen, nicht als persönliches Versagen der beteiligten Autoren – können die Fehler doch erst nach deren Finden behoben werden. Dies verbessert die Qualität des Testobjekts. Dem Management kann dieser Sachverhalt ebenfalls als eine Verminderung des allgemeinen Produktrisikos auf positive Weise kommuniziert werden.
aus Fehlern lernen
Ein positiver Seiteneffekt ist die Möglichkeit, dass der Entwickler anhand erkannter konkreter Fehlerbilder seine eigenen Fähigkeiten verbessert. Aus Fehlern kann und sollte man lernen!
Ebenso spielt auch die Art der Dokumentation eine Rolle bei der Kommunikation. Testergebnisse und andere Resultate sind stets neutral und faktenorientiert zu dokumentieren. Fehlerberichte und Reviewergebnisse müssen objektiv und tatsachenbasiert verfasst werden.
sich in die Rolle der anderen
Fraktion hineinversetzen
Ein weiterer wichtiger Aspekt für die konstruktive Zusammenarbeit von Entwicklern und Testern ist die Fähigkeit, sich in die Rolle des anderen hineinversetzen zu können. Entwickler benötigen Testwissen und Tester benötigen Kenntnisse aus der Entwicklung. Dies ist sowohl fachlich von erheblichem Nutzen als auch bedeutsam für eine konstruktive Umgangsweise miteinander. Entwickler erwerben die Möglichkeit, ihre Testmethodik über die eines klassischen Entwicklertests hinaus zu verbessern und ihre Arbeit aus dem Blickwinkel der Testergruppe zu betrachten. Einer bei Entwicklertests häufig anzutreffenden Blindheit gegenüber den eigenen Fehlern kann hierdurch entgegengetreten werden.
1.5 Zusammenfassung
▶Man unterscheidet abhängig von der Ausführung des Testobjekts zwischen dynamischem und statischem Test mit jeweils vielen Unterausprägungen.
▶Ein Test zeigt als falsifizierendes Verfahren nur die Anwesenheit von Fehlern, nie deren Abwesenheit. Es gibt aber Verfahren, mit denen man das (Nicht-)Vorliegen von geforderten Eigenschaften beweisen kann.
▶Das wichtigste dynamische Testverfahren ist der Funktionstest gegen die Spezifikation, bei statischen Tests sind es Reviews und statische Codeanalysen. Oft ist es sinnvoll, mehrere Verfahren zu kombinieren.
▶Testen beansprucht einen großen Anteil der Entwicklungsressourcen. Es gibt je nach Risiko und Randbedingungen des Projekts beim Testaufwand einen optimalen Kompromiss zwischen Testaufwand und Reifegrad, den man aus wirtschaftlichen Gründen eingehen muss.
▶Beachten Sie die sieben Grundsätze des Testens!
▶Nur ein kleiner Teil des Umfangs einer Software spiegelt die eigentliche Funktion wider. Der größte Teil widmet sich der Robustheit und Fehlerbehandlung bzw. -vermeidung.
▶Psychologische Aspekte spielen beim Testen und vor allem bei der Kommunikation der Ergebnisse eine wesentliche Rolle.