Grundlagen der Informatik 1

Slides:



Advertisements
Ähnliche Präsentationen
Algorithmen und Datenstrukturen
Advertisements

Software Engeniering II
Ziele von EINI I + II Einführen in „Informatik“
der Universität Oldenburg
der Universität Oldenburg
der Universität Oldenburg
Integrations- und Funktionstests im Rahmen des V-Modelles
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Kapselung , toString , equals , Java API
Spec# Proseminar Assertions im SS 2007 Uni Paderborn Andreas Martens Betreuer: Dipl. Inform. Björn Metzler.
Ausnahmen HS Merseburg (FH) WS 06/07.
Dynamische Testverfahren
LE LM 10 - LO3 Verfahren zur Qualitätssicherung
Universität Stuttgart Institut für Kernenergetik und Energiesysteme Was ist Refactoring? Bevor man die Integration angeht, mag es angebracht sein, den.
Erfahrungen aus Tests komplexer Systeme
Universität Stuttgart Institut für Kernenergetik und Energiesysteme Aufgaben des Testens Vergleich des Verhaltens einer Software mit den an sie gestellten.
es gibt (fast) nichts, was nicht anders gemacht werden könnte
Java: Objektorientierte Programmierung
Java: Dynamische Datentypen
Indirekte Adressierung
Java: Referenzen und Zeichenketten
Java: Grundlagen der Objektorientierung
Konstruktoren.
Polymorphie (Vielgestaltigkeit)
Dynamischer Speicher. In einer Funktion wird z.B. mit der Deklaration int i; Speicher auf dem sogenannten Stack reserviert. Wenn die Funktion verlassen.
Algorithmentheorie 04 –Hashing
WS Algorithmentheorie 13 - Kürzeste (billigste) Wege Prof. Dr. Th. Ottmann.
Dynamische Programmierung (2) Matrixkettenprodukt
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
WS Algorithmentheorie 08 – Dynamische Programmierung (2) Matrixkettenprodukt Prof. Dr. Th. Ottmann.
Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 2 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.
Agenda Einführung Haskell QuickCheck Zusammenfassung
Universität Dortmund, Lehrstuhl Informatik 1 EINI II Einführung in die Informatik für Naturwissenschaftler und Ingenieure.
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Kapitel 7 Claudio Moraga, Gisbert Dittrich FBI Unido
Universität Dortmund, Lehrstuhl Informatik 1 EINI II Einführung in die Informatik für Naturwissenschaftler und Ingenieure.
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
Universität Dortmund, Lehrstuhl Informatik 1 EINI II Einführung in die Informatik für Naturwissenschaftler und Ingenieure.
Imperative Programmierung Funktionen und Parameter
Institut für Kartographie und Geoinformation Prof. Dr. Lutz Plümer Diskrete Mathematik I Vorlesung Listen-
Programmieren mit JAVA
PKJ 2005/1 Stefan Dissmann Ausblick Es fehlen noch: Möglichkeiten zum Strukturieren größerer Programme Umgang mit variabler Zahl von Elementen Umgang mit.
PKJ 2005/1 Stefan Dissmann Rückblick auf 2005 Was zuletzt in 2005 vorgestellt wurde: Klassen mit Attributen, Methoden und Konstruktoren Referenzen auf.
Zusammenfassung Vorwoche
Fehlerabdeckung/ Regressionstest1 Testen und Analysieren von Software Fehlerbehebung und Re-Engineering Fehlerabdeckung/ Regressionstest Vortragende:
DVG Klassen und Objekte
Weiteres Programm Studium des Breitendurchlaufs Hierzu
Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.
Einführung in die Programmierung Datensammlung
Telecooperation/RBG Technische Universität Darmstadt Copyrighted material; for TUD student use only Grundlagen der Informatik I Thema 14: Schrittweise.
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Abteilung für Telekooperation Übung Softwareentwicklung 1 für Wirtschaftsinformatik Dr. Wieland Schwinger
Javakurs FSS 2012 Lehrstuhl Stuckenschmidt
Effiziente Algorithmen
Effiziente Algorithmen
Telecooperation/RBG Technische Universität Darmstadt Copyrighted material; for TUD student use only Grundlagen der Informatik I Thema 16: Ausnahmebehandlung.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Informatik II Grundlagen der Programmierung Programmieren in C Programmstrukturen / Kontrollstrukturen Hochschule Fulda – FB ET Sommersemester 2014.
PHP: Operatoren und Kontrollstrukturen
1 Albert-Ludwigs-Universität Freiburg Rechnernetze und Telematik Prof. Dr. Christian Schindelhauer Informatik III Christian Schindelhauer Wintersemester.
Korrektheit von Programmen – Testen
Java Syntaxdiagramme Buchstabe A B Z a z ... Ziffer
Robuste Programme durch Ausnahmebehandlung
Korrektheit von Programmen – Testen
Wieland Schwinger Softwareentwicklung 2 Assertions.
Funktionen. Aufgabe : Eingabe zweier Zahlen ---> Minimum bestimmen Dann nochmals Eingabe zweier Zahlen ---> Minimum bestimmen.
Java Programme nur ein bisschen objektorientiert.
Implementieren von Klassen
 Präsentation transkript:

Grundlagen der Informatik 1 Thema 19: Testverfahren Prof. Dr. Max Mühlhäuser Dr. Guido Rößling

Inhaltsverzeichnis Einführung in die Qualitätssicherung Design by Contract Strukturelle und funktionale Testmethoden

Qualitätssicherung: Motivation Dieser Code in java.util.Arrays hatte 9 Jahre einen Bug: public static int binarySearch(int[] a, int key) { int low = 0; int high = a.length - 1; while (low <= high) { int mid = (low + high) / 2; int midVal = a[mid]; if (midVal < key) low = mid + 1; else if (midVal > key) high = mid - 1; else return mid; // key found } return -(low + 1); // key not found.

Qualitätssicherung: Motivation Informatik-Katastrophen Röntgengerät Therac-25 (1985-1987) Inkorrekte Dosierung, unklare Fehlermeldung und manuelle Kontrollmöglichkeit („manual override“) Mehrere Patienten wurden verletzt oder starben Ariane Flug 501 (1996, $500.000.000) Verlust von Rakete und Satellit Überlauf beim Konvertieren einer double nach short Die „Rakete war zu schnell“ AT&T Telefonnetz (1990) Kettenreaktion durch zu schnelles „ping“ nach Neustart von Switches Airbus Crash 1993 Bodenkontakt bei Landung von Software nicht erkannt, daher zu spätes Bremsen viele, viele mehr... Therac-25: http://courses.cs.vt.edu/~cs3604/lib/Therac_25/Therac_1.html Ariane: http://www.ima.umn.edu/~arnold/disasters/ariane.html AT&T: http://www.phworld.org/history/attcrash.htm "Wartealgorithmus" for die Ariane 4 war in der Ariane 5 Flugphase noch aktiv. Airbus: Flugzeug dachte es fliegt noch Therac: falsche Dosierung durch selten vorkommende Bedienung (Strahlenartkorrektur) und durch Zählerüberlauf bei der Strahpositionierung Mariner Raumsonde, AT&T Telefonswitch Bug: $ 75 Millionen bei AT&T (ohne Folgeschäden) Der break-Befehl of C wurde falsch eingesetzt / Existierte seit Programm-Optimierung 4 Wochen vorher

Entfernung von Fehlern Qualitätssicherung Zwei komplementäre Ansätze Konstruktiver Ansatz Ziel: fehlerfrei durch Konstruktion z.B. Funktionskomposition Analytischer Ansatz Ziel: Fehlerfreiheit nachweisen bzw. vorhandene Fehler finden Überprüfen des Produkts, z.B. mit Konsistenzchecks Vergleich mit der Spezifikation Validierung durch den Kunden ok Definition Design Implemen- tierung Entfernung von Fehlern Konstruktives Ziel muss es sein, fehlerfrei Programme zu entwickeln; analytisches Ziel muss es sein, die Fehlerfreiheit eines Programms nachzuweisen bzw. vorhandene Fehler zu finden.

Warum wir analysieren müssen... Solange Software von Menschen erstellt wird, wird sie auch fehlerhaft sein. Fehler zu machen ist menschlich… Fehler müssen gefunden werden, bevor sie Folgen haben Wo sind meine Krawattennadeln? Hunger!

Wie intensiv müssen wir analysieren? Wie kann man gründlich suchen? Ab wann kann man davon ausgehen, dass mit hoher Wahrscheinlichkeit keine Fehler mehr vorhanden sind? Habe ich jetzt alle? Wird´s bald?!

Was ist die Aufgabe der Analyse? Nur das Aufspüren von Fehlern! Die Ursachenforschung und die letztendliche Beseitigung (Debugging) ist eine andere Disziplin. Irgendwo ist noch eine! Und jetzt? 

Spektrum der Fehler & Analyseansätze Mögliches Spektrum der Fehler nicht dramatisch: Klienten sind an niedrige Qualität gewöhnt ungewollt: Klienten können verloren gehen nicht tolerierbar: können Schaden anrichten Abhängig von der Notwendigkeit Fehler zu vermeiden werden verschiedene Qualitätssicherheitsmethoden eingesetzt. Formale Empirische

Verschiedene Ansätze  Hypothese "Alle ungeraden Zahlen größer 1 sind Primzahlen" Mathematiker: 3 OK, 5 OK, 7 OK; über Induktion folgt, dass alle ungerade Zahlen prim sind. Physiker: 3 OK, 5 OK, 7 OK, 9 Messfehler, 11 OK, 13 OK, … Ingenieur: 3 ist eine Primzahl, 5 ist eine Primzahl, 7 ist eine Primzahl, 9 ist eine Primzahl, 11 ist eine Primzahl, … Informatiker: 112 ist eine Primzahl, 1012 ist eine Primzahl, 1112 ist eine Primzahl, … Vorsicht! Dies ist nur Satire. 

Qualitätssicherungsmethoden analytische Qualitätskontrolle Testverfahren dynamische Tests Strukturelles Testen Funktionales Testen Analysemethoden Verifikation statische Analyse Review (z.B., Inspektion) z.B. Java Compiler: analysiert, ob Variablen initialisiert sind und ob return Anweisungen vorhanden sind.

Qualitätssicherungsbegriffe Verifikation: „Bauen wir die Software richtig?“ Beweis, dass ein Produkt im Sinne der Spezifikation korrekt ist (funktionaler Test als grobe Annäherung) Validierung: „Bauen wir die richtige Software?“ Nachweis, dass ein Produkt in einer bestimmten Zielumgebung lauffähig ist und das tut, was der Benutzer wünscht Ein verifiziertes Programm muss nicht den Wünschen entsprechen Ein validiertes Programm muss nicht korrekt im Sinn der Spezifikation sein

Das Analysedilemma Ansätze in umgekehrter Reihenfolge der Strenge Nicht systematische „Methoden" sind nicht akzeptabel „Überprüfen hier und da" (ad-hoc testing) ist nicht geeignet, um genügend Zuversicht zu gewinnen. Systematisches Testen (coverage testing) könnte ausreichend sein, kann aber die Abwesenheit von Schlupflöchern nicht garantieren. Formale Verifikation (formal verification) könnte die absolute Korrektheit garantieren, aber… Testen kann nur die Gegenwart von Fehlern zeigen, aber niemals deren Abwesenheit. (Dijkstras Gesetz) Beware of bugs in the above code; I have only proved it correct, not actually tried it. (Donald Knuth)

Das Analysedilemma Murphy‘s Law If something can go wrong, it will— at the worst possible moment. If nothing can go wrong, it still will. If nothing has gone wrong, you have overlooked something. Murphy‘s Law Blinn‘s These: Jedes funktionierende Computergrafik- programm enthält eine gerade Anzahl von Vorzeichenfehlern

Wert & Kosten des Testens Entdeckungsrate Aufspüren von Fehlern wird immer schwieriger mit der Zeit Tests / Gefundene Fehler Testen immer aufwändiger Fehlerqualität Fehler immer hartnäckiger Kombination Kosten pro gefundenen Fehler steigen überproportional Aufhören, wenn es zu schwierig/ teuer wird, weitere Fehler zu finden? Zeit

Notwendigkeit einer Systematik In der Praxis gibt es folgende (nicht zufrieden stellende) Gründe mit dem Testen aufzuhören: Zeit aufgebraucht – der Kunde möchte das Produkt Alle vorhandenen Tests werden bestanden Wir brauchen objektive Kriterien, ob noch zusätzliche Testfälle benötigt werden  Testmethoden Das praktische Testen mit JUnit haben wir schon betrachtet

Inhaltsverzeichnis Einführung in die Qualitätssicherung Design by Contract Strukturelle und funktionale Testmethoden

Design by Contract (Entwurf gemäß Vertrag) Wir erinnern uns: Wir hatten Kontrakte schon bei Scheme benutzt Definieren einen Kontrakt für jede Funktion oder bzw. hier Operation Teilnehmer des Kontrakts: aufrufende und aufgerufene Klasse Kontrakt bedeutet Vorteile und Verpflichtungen für beide Seiten “Wenn du mir versprichst, op mit erfüllter vorbed aufzurufen, dann verspreche ich im Gegenzug einen Endzustand zurückzuliefern, in dem nachbed erfüllt ist.”

Design by Contract Ein Kontrakt besteht aus: Vorbedingung: vor der Ausführung eine bestimmten Operation Nachbedingung: nach dem Ausführen einer bestimmten Operation Interne Invarianten: in einem Bereich (in einer Methode) Kontrollflussinvarianten: ein bestimmter Kontrollfluss muss / darf nicht betreten werden Klasseninvarianten: immer wahr vor und nach jeder Operation Aber nicht unbedingt während sich das Objekt in Transition befindet Effektbeschreibung: Beschreibt die Semantik der Operation und ihre (Seiten-)Effekte

Design by Contract Vergleich Scheme und Java In Scheme mussten wir Argumenttypen überprüfen In Java müssen wir Argumenttypen nur in wenigen Fällen prüfen z.B. ein Object[]-Feld, in dem nur bestimmte Subtypen von Object in dem Feld erwartet werden Design-by-Contract und OOP Die Methoden von Subtypen erben die Kontrakte der Methode des Supertyps. Haben gleiche Vor- und Nachbedingungen Können stärkere Vor- und Nachbedingungen haben Werfen die selben Ausnahmen Können weiter spezialisierte Ausnahmen werfen

Assertions (Zusicherungen) Das assert Schlüsselwort ist eine einfache Sprachunterstützung für Design by Contract in Java seit Java 1.4 Wir können Assertions verwenden, um die Benutzung der Objektschnittstelle einzuschränken Den inneren Zustand eines Objekts einzuschränken Assertions haben folgende Form: assert booleanExpression [: Expression]; Wenn booleanExpression true ist, wird das Programm normal weiter ausgeführt. Andernfalls wird ein AssertionError geworfen. Expression ist ein optionaler Ausdruck, der zur Laufzeit ausgewertet und mit der Fehlernachricht zusammen ausgegeben wird. Die AssertionError Klasse ist ein Subtyp der Error Klasse. Ähnlich der RuntimeException muss diese nicht gefangen und auch nicht deklariert werden. Im Normalfall sind Assertions deaktiviert, man kann sie aktivieren über: > java –ea <classfile name>

Assertions verwenden assert wird benutzt, um folgendes auszudrücken: Vor- und Nachbedingungen, Interne Invarianten, Kontrollflussinvarianten, Klasseninvarianten In folgenden Fällen ist Vorsicht geboten: Verwenden Sie keine Assertions für das Prüfen von Argumenten in öffentlichen Methoden Verwenden Sie keine Assertions, um bestimmte Aufgaben durchzuführen, die das Programm für eine korrekte Ausführung benötigt Die Zusicherung wird nur ausgewertet, falls sie aktiviert ist! Assertions sollten keine Seiteneffekte auf die normale Programmausführung haben

Assertions für Vorbedingungen verwenden Eine Ausnahme bei den Vorbedingungen für öffentliche Methoden: Verwenden Sie keine Assertions für das Prüfen von Argumenten in öffentlichen Methoden  Die veröffentlichte Spezifikation (oder Kontrakt) muss immer eingehalten werden  Die allgemeingültige Konvention ist, eine entsprechende Laufzeit-Ausnahme zu verwenden: meist eine IllegalArgumentException Vorbedingungen für private Methoden: public void setAttr(int attr) { if (attr <= 0 || attr > MAX_VALUE) throw new IllegalArgumentException("illegal value:"+attr); attribute = attr; } private void setAttr(int attr) { assert attr > 0 && attr <= MAX_VALUE: "value for attr is not in interval:"+attr; attribute = attr; }

Assertions für Nachbedingungen verwenden Nachbedingungen sowohl für private als auch für öffentliche Methoden: public String reverse(String oldStr) { String newStr = ""; // verschiebe in umgekehrter Reihenfolge // jeden char des alten in den neuen String assert oldStr.equals("") : "oldStr must be empty"; return newStr; }

Assertions verwenden Aussagen über interne Invarianten: Aussagen über Kontrollflussinvarianten: if (i % 3 == 0) { ... } else if (i % 3 == 1) { } else { assert i % 3 == 2 : i; } Muss nur im else-Block erfüllt sein void foo() { for (...) { if (...) return; // sollte irgendwann zutreffen } assert false : "Execution should never reach this point!" Schlägt immer fehl, wenn erreicht!

Assertions für Klasseninvarianten/ Effektbeschreibungen verwenden Eine Klasseninvariante ist eine besondere Aussage, die für jede Instanz der Klasse zu jeder Zeit gilt Außer die Instanz befindet sich gerade in Überführung von einem konsistenten Zustand zum nächsten Beziehung über mehrere Attribute Sollte vor und nach jedem Methodenaufruf wahr sein Jede öffentliche Methode und jeder Konstruktor enthält eine Assertion unmittelbar vor dem Rücksprung (bei jedem „Austrittspunkt“)

Assertions für Klasseninvarianten/ Effektbeschreibungen verwenden In Java benutzen wir JavaDoc, um Effekte zu beschreiben: /** * Erhöht den counter Feld um den Wert des step-Parameters. * <p>Effekte: der counter wird geändert.</p> * @param step Wert, um den der Zähler erhöht werden soll. * @return Gibt den aktuellen Wert der counter-Variable zurück. */ public int inc(int step) { counter += step; return counter; }

Die Kopie wird nur durchgeführt, wenn Assertions aktiviert sind Komplexe Assertions Wir verwenden innere Klassen für komplexe Assertions: void foo(final int[] array) { // Innere Klasse sichert den Zustand und fuehrt endgueltige // Konsistenzpruefung durch class DataCopy { private int[] arrayCopy; DataCopy() { arrayCopy = (int[]) array.clone(); } boolean isConsistent() { return Arrays.equals(array, arrayCopy); DataCopy copy = null; // Immer erfuellt; speichert als Seiteneffekt eine Kopie des Felds assert ((copy = new DataCopy()) != null); ... // Manipulieren des Felds // Stellt sicher, dass das Feld immer noch gleich aussieht assert copy.isConsistent(); Die Kopie wird nur durchgeführt, wenn Assertions aktiviert sind

Design by Contract zusammengefasst Möglichkeiten: Vereinfachung: Wir definieren ein Korrektheitskriterium nur für einen bestimmten Teil des Programms. Erwartung: Solange alle Teile des Programm die Kontrakte erfüllen, wird das Programm (fast) korrekt funktionieren. Hoffnung: Wenn ein Fehler auftritt, dann können wir diesen durch eine Verletzung eines Kontrakts aufspüren. Einschränkungen: Auch wenn jeder Kontrakt erfüllt ist, kann es sein, dass das Programm nicht richtig funktioniert Ein Kontrakt kann semantisch falsch sein Wir haben vergessen, eine bestimmte Bedingung im Kontrakt anzugeben. Wir brauchen andere Herangehensweisen, um diese Fälle abzufangen!

Inhaltsverzeichnis Einführung in die Qualitätssicherung Design by Contract Strukturelle und funktionale Testmethoden

Testmethode Planmäßiges, auf einem Regelwerk aufbauendes Testverfahren. Im Allgemeinen eine systematische Methode zur Auswahl und/oder Generierung von Tests. Strukturelle Testmethoden Funktionale Testmethoden Testverfahren sind nur mit entsprechender Werkzeugunterstützung sinnvoll anwendbar.

Grundbegriffe Testdatum Eingabewert, der einen Eingabeparameter oder eine Eingabevariable des Testobjekts mit einem Datum versorgt. Testfall Ein Satz von Testdaten, der die vollständige Ausführung eines zu testenden Programms bewirkt sowie das Sollresultat. Testsuite Eine nicht-leere Menge von Testfällen, die nach einem bestimmten Kriterium ausgewählt wurden. Testtreiber Handelt es sich bei dem Prüfling um eine Operation in einer Klasse, dann kann sie nicht direkt getestet werden. Der Tester muss für die jeweilige Klasse einen Testrahmen programmieren, der ein interaktives Aufrufen der Operationen ermöglicht.

Grundbegriffe: Instrumentierung Schlägt ein Testfall fehl, muss man nachvollziehen, welche Anweisungen des Testobjekts mit dem Testfall durchgeführt wurden  Fehler lokalisieren Werkzeugunterstütztes Mitprotokollieren, welche Teile des Prüflings bei der Ausführung durchlaufen wurden Nach dem Testlauf werden die Zählerstände ausgewertet. int maximum(int a, int b) { if (a > b) { thenBranch = true; return a; } else { elseBranch = true; return b; } In den Quellcode eingefügte Zähler In den Quellcode eingefügte Zähler

Grundbegriffe: Testabdeckung Testabdeckung: Verhältnis zwischen tatsächlich ausgeführten Testfällen und theoretisch existierenden Testfällen ausgeführte Testfälle existierende Testfälle Bei einer Testabdeckung von 100% spricht man von einem erschöpfendem Test Erschöpfendes Testen ist meist weder praktikabel noch sinnvoll Testende-Kriterium legt fest, welches Minimalziel durch die Testfälle erreicht werden soll TAB = Kriterium für die Auswahl von Testfällen!

Testmethoden Strukturelle Testmethoden: basieren auf der Programmstruktur (White-Box Tests) Testfälle werden nur aus der Programmstruktur abgeleitet Die Vollständigkeit der Prüfung wird anhand von Strukturelementen (Anweisungen, Zweige, Daten) des Prüfgegenstands bewertet Die Spezifikation des Prüfgegenstands wird bei der Beurteilung der Vollständigkeit der Tests nicht beachtet Funktionale Methoden: Basieren auf den Anforderungen (Black Box Tests) Im Idealfall werden die Anforderungen in einer formellen Spezifikation ausgedrückt Testfälle und Testdaten werden aus der funktionalen Spezifikation des Prüfgegenstands abgeleitet Vollständigkeit der Prüfung wird anhand der Spezifikation bewertet Struktur des Prüfgegenstands wird nicht ausgewertet

Testmethoden Überblick Klassifikation nach benötigter Information Black Box Grey Box White Box strukturelles Testen funktionales Testen Grenzwert- analyse kontrollfluss- orientierte Testverfahren Äquivalenz- klassenbildung Benutzungs- fälle überprüfen Zufallstest datenfluss- orientierte Theoretisch reine „BlackBox“ Verfahren. In der Praxis auch Heranziehen der Struktur.

Kontrollflussgraphen Kontrollflussgraph wird durch einen gerichteten Graphen dargestellt Jeder Knoten stellt eine Anweisung dar Die Knoten sind durch gerichtete Kanten verbunden Die Anzahl ausgehender Kanten eines Knotens heißt Ausgangsgrad Kantenfolgen beschreiben einen möglichen Kontrollfluss von Knoten i zu Knoten j Zweige Gerichtete Kanten Pfad Folge von Knoten und Kanten, die mit dem Startknoten beginnt und mit einem Endknoten endet

Kontrollflussgraphen Kontrollflussgraphen besitzen grundsätzlich einen Start- und einen Endknoten Der Endknoten hat den Ausgangsgrad 0 Jeder normale Knoten liegt auf einem Pfad vom Startknoten zum Endknoten Knoten mit Ausgangsgrad 1 heißen Anweisungsknoten Alle anderen Knoten außer dem Endknoten heißen Prädikatknoten

Kontrollflussgraphen (Beispiel) nstart n2 n2 c = System.in.read(); // n1 while (c >= 'A' && c <= 'Z' && totalNumber<5) { // n2 totalNumber = totalNumber + 1; // n3 if (c=='A'||c=='E'||c=='I'||c=='O'||c=='U') { // n4 vowelNumber = vowelNumber + 1; // n5 } c = System.in.read(); // n6 n3 n4 n5 n6 nende

Überdeckungstestverfahren Strukturelemente wie Anweisungen, Zweige oder Bedingungen werden genutzt, um Testziele zu definieren Beispiel: Das Anweisungsüberdeckungstestverfahren hat als Ziel, Testdaten so zu wählen, dass alle Anweisungen im Programm ausgeführt werden Realisierung von Überdeckungstests Quellcode wird instrumentiert Bei der Ausführung erzeugen die Trace Statements ein Logbuch Auswertung des Logbuchs zur Erstellung eines Überdeckungsberichts

Überdeckungstests Übersicht... Pfadüberdeckung Bedingungsüberdeckung mehrfach Zweigüberdeckung Bedingungsüberdeckung einfach Anweisungsüberdeckung A B  A impliziert B

Anweisungsüberdeckung (C0) nstart nende Ziel: Mit einer Anzahl von Testfällen alle vorhandenen Anweisungen auszuführen Beispiel eines Testfalls, der das Kriterium erfüllt Eingabe: <'A', '#'> Hilft, toten Code zu finden Ist notwendig aber nicht ausreichend Besitzt wenig praktische Relevanz

Zweigüberdeckung (C1-Test) Ziel: Die Ausführung aller Zweige, d.h. aller Kanten des Kontrollflussgraphen Wird auch Entscheidungsüberdeckung genannt: Jede Entscheidung muss mindestens einmal die Wahrheitswerte wahr und falsch annehmen. Metrik: Hilft, nicht erreichbare Verzweigungen zu finden Anweisungsüberdeckung vollständig enthalten Betrachtet weder Kombination von Verzweigungen noch komplexe Bedingungen Gilt als das minimale Testkriterium Czweig = Anzahl der ausgeführten Zweige Anzahl der vorhandenen Zweige

Zweigüberdeckung (C1): Beispiel <'A','#'> n1 n2 n3 n4 n6 nstart nend n5 n n1 n2 n3 n4 n6 nstart nend n5

Einfache Bedingungsüberdeckung (C2) nstart alle atomaren Bedingungen mind. einmal true & false n1 Testfälle <'A', 'E', 'I', 'O', 'U', '@'> <'€‘> Enthält weder Anweisungs- noch Zweigüberdeckung Da beides minimale Testkriterien sind, ist eine alleinige einfache Bedingungsüberdeckung nicht ausreichend n1 n2 n2 n3 n3 n4 n4 n5 n5 if (c=='A' || c=='E' || c=='I' || c=='O' || c=='U') n6 n6 Der „else“ Zweig wird nie beschritten nende

Mehrfache Bedingungsüberdeckung nstart Alle Kombinationen von atomaren Bedingungen abdecken Testfälle <'A', 'E', 'I', 'O', 'U', '@'> <'A', 'E', 'I', 'O', 'U', '€'> <'A', 'E', 'I', 'O', 'U', 'A'> <'A', '@'>, <'A', '€'> Zweigüberdeckung enthalten Kombinationsexplosion: N atomare Bedingungen  2N Möglichkeiten Kombinationen teilweise unmöglich! Auswertung wird eventuell abgebrochen (Lazy-Evaluation = nicht-strikte Auswertung) n1 n1 n2 n2 n3 n3 n4 n4 n5 n5 n6 n6 nende

Pfadüberdeckung (C7) nstart Testfälle Alle Kombinationen von Zweigen abdecken Testfälle <'#'>, <'B', '#'>, <'A', '#'> <'A', 'B', 'B', 'B', 'B'> <'B', 'A', 'B', 'B', 'B'>, … Zweigüberdeckung enthalten Schleifen führen praktisch zu unendlichen vielen Testfällen Höchste Erfolgsaussichten, aber völlig unpraktikabel n1 n1 n2 n2 n3 n3 n4 n4 n5 n5 n6 n6 nende

Produkt wird gegen sich selbst geprüft Überdeckungstests Vorteile Defizite in der Teststrategie aufdecken Nicht erreichbaren Code entdecken Anhaltspunkt für den Testfortschritt Nachteile 100% Abdeckung nicht praktikabel Codeabdeckung ist kein Kriterium für vollständiges Testen Fehlende Funktionalität wird nicht erkannt Produkt wird gegen sich selbst geprüft

Funktionales Testen Motivation: Es ist unzureichend, ein Programm lediglich gegen sich selbst zu testen  Testfälle aus Programmspezifikation ableiten Programmstruktur wird nicht betrachtet Ziel Möglichst umfassende, aber redundanzarme Prüfung der spezifizierten Funktionalität Überprüfung aller Programmfunktionen (Funktionsüberdeckung)

Funktionales Testen Hauptschwierigkeiten Ziel der Testplanung Ableitung der geeigneten Testfälle Vollständiger Funktionstest ist im allgemeinen nicht durchführbar Ziel der Testplanung Testfälle so auswählen, dass die Wahrscheinlichkeit groß ist, Fehler zu finden Testfallbestimmung Funktionale Äquivalenzklassenbildung Grenzwertanalyse

Testmethoden im Überblick Black Box Grey Box White Box strukturelles Testen funktionales Testen Grenzwert- analyse kontrollfluss- orientierter Test Äquivalenz- klassenbildung Benutzungs- fälle überprüfen Zufallstest datenfluss- orientierter Test Testklassifizierung nach benötigter Information

Äquivalenzklassenbildung Motivation: Die Anzahl von Testfällen wird sinnvoll eingeschränkt, indem die Testdaten in Äquivalenzklassen zerlegt werden Annahme: Für jeden Repräsentanten aus einer Äquivalenzklasse reagiert das Programm auf die gleiche Weise Beispiele Klassen: Vokal Konsonant kein Buchstabe Repräs.: E T % Klassen: x<0 0x5 x>5 Repräs.: -5 3 9

Grenzwertanalyse Motivation: Fehler treten typischerweise bei Extremwerten bzw. an den „Rändern“ von Äquivalenzklassen auf. Annahme: Die „extremen“ Repräsentanten sind nicht nur ebenso wirksam wie „normale“ Repräsentanten, sondern darüber hinaus besonders geeignet. Beispiele Klassen: x<0 0x5 x>5 Repräs.: -1 0 und 5 6

Testmethoden: Zusammengefasst Strukturelles Testen Findet: Abbruchfehler, unerreichbare Zweige, Endlosschleifen, inkonsistente Bedingungen  Prüfung gegen sich selbst Funktionales Testen Findet: Falsche Antworten  Prüfung gegen Spezifikation

Kombinierte Strategie Strukturelle Verfahren haben einige Nachteile Nicht implementierte Funktionen werden nicht aufgespürt Das Ziel der Überdeckung produziert oft bzgl. der Funktionalität triviale Testfälle Auch funktionale Verfahren haben einige Nachteile Basieren auf der Spezifikation, die ein höheres Abstraktionsniveau besitzt als die Implementierung Achillesfersen der Implementierung werden nicht herangezogen Typischerweise höchstens 70% Zweigüberdeckung Die Verfahren ergänzen sich gegenseitig! Die Vorteile des einen Ansatzes können benutzt werden, um die Nachteile des anderen Ansatzes abzudecken  Verwenden eines kombinierten Ansatzes

Kombinierte Strategie Zunächst Funktionstest... Anhand der Spezifikation Äquivalenzklassen bilden Grenzwerte ermitteln Testfälle erstellen  Funktionsumfang systematisch geprüft ...anschließend Strukturtest Oben erzielte Überdeckung analysieren Nicht benutzte Bedingungen oder Pfade identifizieren Gewünschte Überdeckung erzielen (einigermaßen) Robustheit des Produkts sichergestellt & extra Validierung der Spezifikation (zu einem gewissen Grad)

Testen von Klassen und Unterklassen Die kleinste, unabhängige prüfbare Einheit objektorientierter Systeme ist die Klasse Da die Operationen einer Klasse stark voneinander abhängen, ist es nicht sinnvoll, sie einzeln zu testen. Der Test hängt davon ab, welche Art von Klasse vorliegt: Normale Klasse Abstrakte Klasse

Test normaler Klassen Erzeugung eines instrumentierten Objekts der zu testenden Klasse Nacheinander Überprüfung jeder einzelnen Operation für sich. Zuerst sollen diejenigen Operationen überprüft werden, die nicht zustandsverändernd sind. Dann werden die zustandsverändernden Operationen getestet. Durch Äquivalenzklassenbildung und Grenzwertanalyse werden aus den Parametern Testfälle abgeleitet Das Objekt muss vorher in einen für diesen Testfall zulässigen Zustand versetzt werden durch vorhergehende Testfälle oder eine gezielte Initialisierung vor jedem Testfall Nach jeder Operationsausführung muss der neue Objektzustand geprüft und der oder die Ergebnisparameter mit den Sollwerten abgeglichen werden. Da diese Operationen in der Regel sehr einfach aufgebaut sind, können sie leicht formal verifiziert werden

Test normaler Klassen Test jeder Folge abhängiger Operationen in der gleichen Klasse Dabei ist sicherzustellen, dass jede Objektausprägung simuliert wird Alle potentiellen Verwendungen einer Operation sollten unter allen praktisch relevanten Bedingungen ausprobiert werden Anhand der Instrumentierung ist zu überprüfen, wie die Testüberdeckung aussieht Fehlende Überdeckungen sollten durch zusätzliche Testfälle abgedeckt werden.