Fehler Fehlersuche, Logging, Debugging, Testen ■ Ein Fehler ist eine Abweichung von einem optimalen oder normierten Zustand oder Verfahren in einem bezüglich seiner Funktionen determinierten System [wikipedia] ■ Fehler im Zusammenhang mit Software entstehen durch ■ ■ ■ Sie wissen, was ein Fehler (Bug) ist ■ ■ Sie wissen, wie man Fehler findet ■ Mangelhafte nicht aufgabenadäquate Programmspezifikation Mangelhafte Usability (Benutzungstauglichkeit) Fehlerhaften Dateninput, z. B. falsche Bedienung oder andere Anwendungsfehler: Programmfehler ■ Sie wissen, wie man zusätzliche Information gewinnt ■ Sie wissen, auf welche Arten man Testen kann School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege Programmfehler Fehlerarten ■ Programmfehler bezeichnet ganz allgemein ein Fehlverhalten von Computerprogrammen ■ Syntaxfehler (während Übersetzung erkannt) ■ ■ ■ ■ Mögliche Ursachen ■ ■ ■ Der Programmierer hat einen bestimmten Zustand in der Programmlogik nicht berücksichtigt Die Laufzeitumgebung arbeitet fehlerhaft. Spezifikation hat Unvollständigkeiten, Ungenauigkeiten oder Mehrdeutigkeiten. 2 von 52 überprüft ob Anweisungsfolge formal korrekt der Syntax der verwendeten Programmiersprache konform Je nach Programmiersprache: ■ Variablen deklariert, initialisiert ■ Verwendung entsprechend der Datentypen erlaubt ■ Logische Fehler (meist erst zur Laufzeit erkannt) ■ Der erste Fehler in der Computerei wurde von einer Motte in einem Röhrencomputer 1945 verursacht; Fehler werden oft auch als "Bugs" bezeichnet. ■ Programm läuft aber liefert nicht das gewünschte Resulat Je früher ein Fehler erkannt wird, desto billiger und einfacher wird dessen Behebung School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 3 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 4 von 52 Syntaxfehler Logische Fehler ■ Der Compiler prüft, ob die angegebenen Anweisungen formal korrekt sind. ■ ■ je restriktiver (ev. sogar mit Redundanz) desto grösser ist die Wahrscheinlichkeit, dass Tippfehler nicht falsch interpretiert werden (zu logischen Fehlern führen). z.B. jede Variable deklariert und initialisiert ist ■ Programm läuft, aber es verhält sich nicht so, wie erwartet/wie spezifiziert ■ ■ liefert falsche oder zu ungenaue Resultate unerwarteter Ablauf oder Reaktionen des Programms ■ Beispiele ■ Der Java Übersetzer überprüft: ■ ■ ■ Variablendeklarationen Typenprüfung bei Operationen und Zuweisungen Initialisierung der Variablen ■ ■ ■ ■ © A. Meier/M. Braschler/J. Zeman/K. Rege vergessen das Objekt als Listener anzumelden if- und Schleifen-Konstrukte ohne geschweifte Klammern repaint() vergessen, nach einer Änderung im GUI ■ ■ logische Fehler können auch Exception verursachen Variable/Methode nicht deklariert/sichtbar Variable einen Wert zuweisen, der nicht ihrem Typ entspricht falsch plazierte oder fehlende Klammern fehlende ( ) bei Methodenaufruf ohne Parameter (Pascal-Legacy) fehlender oder falscher import School of Engineering Unendliche Schleife: z.B for (int i = 0; i <10; i--); ■ ■ ■ Hitliste der Tippfehler in Java ■ ■ ■ jede Exception protokollieren ■ Analytisches Vorgehen gefragt 5 von 52 Fehlersuche School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 6 von 52 Vorgehen bei der Fehlersuche ■ Statistische Daten aus der Softwaretechnik besagen, dass - produktiv eingesetzte - Programme im Mittel etwa zwei bis drei Fehler je 1000 Zeilen Code enthalten (Gleich nach erster Compilation pro 100 Quellenzeilen zwischen fünf bis zehn Fehler). ■ Symptom des Fehlers analysieren ■ Fehler bei der Übersetzung: Æ Fehlermeldung des Compiler genau lesen!!! Æ Angabe des Fehlerortes: oft ungenau ■ Ort an dem der Compiler den Fehler erkennt, muss nicht mit dem Ort des Fehlers übereinstimmen. Æ Folgefehler ■ Fehler sind unvermeidlich, jedoch: am meisten lernt man aus Fehlern!! ■ ■ ■ man übt dabei analytisches und logisches Denken ■ ■ Laufzeitfehler: ■ Exceptions Æ Fehlermeldung genau lesen Æ Angabe des Fehlerortes genau studieren (Zeilennummern) ■ Logische Fehler Æ Zuerst Fehler genau beschreiben : ■ was beobachten Sie (nicht, was vermuten Sie, wo das Problem liegt!) ■ Bsp.: "Ich sehe das Quadrat, das gezeichnet werden soll, nicht im Appletfenster" Symptome eines Fehlers geben oft keine klaren Rückschlüsse auf die Ursache Verhalten des Systems „macht keinen Sinn“ Æ man sucht am falschen Ort School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 7 von 52 z.B. "{" vergessen, wird erst viel später erkannt. School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 8 von 52 Lokalisieren des Fehlers Test und Fehlersuche ■ Test ■ ■ ■ Die Lokalisierung des Fehlers ist meist viel schwieriger als die Behebung Prüfen eines Programms (oder eines ganzen Systems), ob es seine Anforderungen erfüllt. Beachte: Ein Test sucht keine Gründe für Fehler, sondern nur deren Vorhandensein! ■ Fehler lokalisieren ■ ■ Fehlersuche ■ ■ ■ ■ Suche nach der Ursache, warum und wo der Grund für einen festgestellten Fehler liegt. Der englische Ausdruck dafür ist Debugging („Entwanzen“) Das Werkzug dazu wird als "Debugger" bezeichnet ■ Behebung ■ manchmal einfach (Zeilennummer bei Exception, Syntaxfehler) bei logischen Fehlern oft schwierig ■ Sich überlegen, wer direkt verantwortlich ist für das Verhalten, das ich erwarte. ■ Bsp. Quadrat: "Quadrat wird in paint()-Methode gezeichnet " ■ Falls nichts mehr hilft: Æ Binäre Suche ■ Auskommentieren von Teilen des Programms ■ Ausschlussverfahren: wo liegt der Fehler sicher nicht Korrektur des Fehlers und wieder Test Durch die Behebung eines Fehlers kann ein neuer eingebaut werden School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 9 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 10 von 52 Zusätzliche Informationen gewinnen ■ Werte der Variablen auf Kommandozeile ausgeben ■ System.out.println(); oder System.err.println(); ■ Programmfluss "trace" ■ ■ System.out.println("foo-enter") System.out.println("foo-leave") beim Eintreten in eine Methode vor Verlassen einer Methode ■ Verwendung eines Frameworks für Laufzeit Meldungen: Logging ■ Steuerung der Menge der Ausgabe ■ Steuerung des Ausgabegerätes/-Formats Laufzeit Meldungen ■ Laufzeitanalyse Werkzeuge ■ Z.B. JConsole ■ Verwendung eines Debuggers ■ ■ ■ Programm kann an spez. Stelle angehalten werden: Breakpoint Programmfluss kann verfolgt werden: single-step, step in, step over Variablen können betrachtet werden School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 11 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 12 von 52 Laufzeit Statusmeldungen Hello Log ■ Status-Meldungen von Programmen ■ Import ImportLogging LoggingKlassen Klassen Ausgabe auf Konsole mit System.out.println reicht nicht ■ Anforderungen ■ ■ ■ ■ ■ ■ ■ import import java.util.logging.*; java.util.logging.*; Die Log Meldung soll mit Zeitstempel versehen sein; Ort der Meldung (Klasse/Methode) Log Meldungen müssen nach Dringlichkeit der Behandlung klassifiziert werden, damit keine Meldungsüberflutung entsteht (aufsteigende Dringlichkeit), z.B. ■ FINE, FINER, FINEST, CONFIG, INFO, WARNING, SEVERE Logs müssen gespeichert/archiviert werden Dezentral anfallende Logs müssen gesammelt und in Korrelation gebracht werden können Log Ausgabe Format muss standardisiert und/oder angepasst werden können Logs müssen (durch Werkzeuge) analysiert werden können Logs müssen zur Laufzeit umgeleitet, ein-/ausgeschaltet werden können Hole eine statische Logger Instanz Hole eine statische Logger Instanz mit dem Namen der eigenen Klasse mit dem Namen der eigenen Klasse public publicclass classMyClass MyClass{ { static staticLogger Loggerlogger logger== Logger.getLogger(MyClass.class.getName( Logger.getLogger(MyClass.class.getName());)); public static void main() { public static void main() { MyClass m = new MyClass(); MyClass m = new MyClass(); m.foo(); m.foo(); }} public publicvoid voidfoo() foo(){ { logger.info("Hello logger.info("HelloWorld"); World"); }} }} ■ Logging Frameworks ■ ■ Ausgabe auf Console Ausgabe auf Console ab java 1.4.2 Teil des JDKs: java.util.logging.Logger älteres aber weiterhin beliebtes Logging Framework Log4J http://logging.apache.org/log4j/docs/ School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 13 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege Funktionalität Logger Klasse ■ Logger basiert auf dem Zusammenspiel von 3 Gruppen von Klassen ■ "Arbeitspferd" des Logging Frameworks public public class class Logger Logger {{ // // Creation Creation && retrieval retrieval methods: methods: public static public static Logger Logger getLogger(String getLogger(String name); name); public void addHandler(Handler public void addHandler(Handler handler); handler); Logger hole holeLogger LoggerInstanz Instanz static void getLogger(String name) setLevel(Level) error(msg) info(msg) log(Level, msg) Handler // // printing printing methods: methods: public public void void finest(Object finest(Object message); message); public public void void finer(Object finer(Object message); message); public void fine(Object message); public void fine(Object message); public public void void config(Object config(Object message); message); public public void void info(Object info(Object message); message); public void warning(Object public void warning(Object message); message); public public void void severe(Object severe(Object message); message); // generic printing method: // generic printing method: public public void void log(Level log(Level l, l, Object Object message); message); Ausgabe-Methoden Ausgabe-Methodenund und Festlegung Festlegungder derLogger Logger Hierarchie Hierarchie Bestimme Bestimme Ausgabemedium Ausgabemedium Formatter Bestimme Bestimme Ausgabeformat Ausgabeformat }} ConsoleHandler School of Engineering FileHandler ... SimpleFormatter © A. Meier/M. Braschler/J. Zeman/K. Rege XMLFormatter ... 15 von 52 14 von 52 Wenn Wennnicht nichtangegeben, angegeben, werden werdenEinstellungen Einstellungenvom vom RootLogger RootLogger==""""übernommen. übernommen. Zuerst Zuerstmuss musseine eineLogger LoggerInstanz Instanz erzeugt erzeugtwerden, werden,mit mit dem demeigenen eigenen Klassenname Klassennameals alsParameter Parameter Ausgabe-Methoden Ausgabe-Methoden public public void void setLevel(Level setLevel(Level l); l); Legt den Detailierungsgrad fest (Default = Info): Ausgabe nur Legt den Detailierungsgrad fest (Default = Info): Ausgabe nur wenn Priority-Level des Loggers >= Log-Level der Meldung wenn Priority-Level des Loggers >= Log-Level der Meldung wird direkt in Logger Instanz oder über Config-Datei gesteuert. wird direkt in Logger Instanz oder über Config-Datei gesteuert. School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 16 von 52 Logger Hierarchie Handlers ■ Die Logger bilden Hierarchie entsprechend Package-Struktur ab ■ Geben an, wohin die Ausgabe geschrieben wird ■ Methode getLogger liefert immer dieselbe Logger-Instanz bei gegeb. Namen ■ Ein Handler bekommt von einem Logger einen Protokolleintrag übergeben ■ Nicht definierte Einstellungen (LogLevel, Handler, Formatter) werden vom Eltern-Logger übernommen ■ Er formatiert diesen über einen Formatter und verarbeitet den formatierten Eintrag rootLogger ch Logger.getLogger(""); Logger.getLogger(""); ■ z. B. in eine Datei geschrieben, oder auf der Konsole ausgegeben oder über Netzwerk verschickt wird. ■ Es können mehrere Handlers gleichzeitig aktiviert sein. com Logger.getLogger("ch"); Logger.getLogger("ch"); Es sind folgende Handlers im JDK definiert ch.zhaw Logger.getLogger("ch.zhaw"); Logger.getLogger("ch.zhaw"); ■ ConsoleHandler Gibt die log Meldung auf der Konsole aus. ■ FileHandler Schreibt die log Meldung in eine Datei. ch.zhaw.MyClass ch.zhaw.YourClass ■ MemoryHandler Legt die log Meldung in einem Puffer ab. ■ SocketHandler Schreibt die log Meldung auf einen Socket. Logger.getLogger(MyClass.class.getName()); Logger.getLogger(MyClass.class.getName()); Logger.getLogger(YourClass.class.getName()); Logger.getLogger(YourClass.class.getName()); School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 17 von 52 ■ StreamHandler Schreibt die log Meldung in einen OutputStream School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege Formatters Konfiguration über Property Datei ■ Bestimmen, wie die Ausgabe formatiert wird. ■ Meist über Konfigurationsdatei: logging.properties ■ Jedem Handler ist ein Formatierer zugeordnet. import import java.io.*; java.io.*; import import java.util.logging.*; java.util.logging.*; sucht ... suchtinin"classpath" "classpath" ... try try {{ InputStream config = ClassLoader.getSystemResourceAsStream("logging.properties"); InputStream config = ClassLoader.getSystemResourceAsStream("logging.properties"); if if (config (config != != null) null) LogManager.getLogManager().readConfiguration(config); LogManager.getLogManager().readConfiguration(config); }} "logging.properties" "logging.properties" ... ... Datei Datei # To also add the Console & FileHandler, use the following line instead. # To also add the Console & FileHandler, use the following line instead. handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler # Default global logging level. # Default global logging level. .level = INFO .level = INFO # Handler specific properties. # Handler specific properties. # default file output is in user's home directory. # default file output is in user's home directory. java.util.logging.FileHandler.pattern = %h/java%u.log java.util.logging.FileHandler.pattern = %h/java%u.log java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.level = ALL java.util.logging.FileHandler.level = ALL java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter # Limit the message that are printed on the console to INFO and above. # Limit the message that are printed on the console to INFO and above. java.util.logging.ConsoleHandler.level = ALL java.util.logging.ConsoleHandler.level = ALL java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # For example, set the com.xyz.foo logger to log all # For example, set the com.xyz.foo logger to log all # messages: # messages: com.xyz.foo.level = ALL com.xyz.foo.level = ALL Es sind folgende Formatter im JDK definiert ■ SimpleFormatter: einfache Ausgabenformatierung als Text ■ XMLFormatter : Ausgabe wird als XML formatiert School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 18 von 52 19 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 20 von 52 JConsole ■ Tool zur Laufzeitüberwachung von Java Prozessen ■ Nützlich um Zustand der Java VM zu analysieren und Einstellungen vorzunehmen ■ ■ Memory und Theads Logger Level zu setzen ■ JConsole auf Kommandozeilen im jdk/bin Verzeichnis starten Laufzeit System Monitoring School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 21 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 22 von 52 Debugger ■ Ein Debugger ist ein Programm, das einem erlaubt, das Programm irgendwo anzuhalten und dort verschiedene Informationen über den Zustand des Programms (v.a. Variablenwerte) anzuschauen. ■ Die meisten integrierten Entwicklungsumgebungen enthalten einen Debugger. ■ Debugger Debugger besitzen i.d.R. eine graphische Benützeroberfläche mit je einem Fenster mit Sourcecode, Programm-Output, selektierten Variablen mit ihren aktuellen Werten ■ Debugger sind gute Werkzeug um einem Fehler auf die Spur zu kommen ■ Aber: findet Fehler nicht selbständig sondern ist nur "Hilfsmittel": a fool with a tool is still a fool School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 23 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 24 von 52 Funktionen eines Debuggers Enable Debugger in JCreator ■ Ein guter Debugger sollte folgende Funktionen unterstützen ■ Enable Debug Toolbar in JCreator ■ Möglichkeiten bieten, sogenannte Breakpoints (Haltepunkte) zu setzen ■ Æ Programm wird an einer vom Benützer definierten Stelle (Programmzeile) im Programm angehalten ■ oder Programm wird angehalten, sobald ein Fehler auftritt ■ Benützer kann nun an dieser Stelle: ■ Variablenwerte abfragen und eventuell neu setzen ■ Programm weiter laufen lassen, eventuell schrittweise ■ Stepping: Schrittweises Durchlaufen des Programms (stepping) ermöglichen ■ Programm hält nach jeder ausgeführten Programmzeile wieder an ■ Dabei kann gewählt werden, ob ■ aufgerufene Methoden ohne Unterbruch ausgeführt werden (step over) oder ■ in die aufgerufene Methode hineingesprungen werden soll (step in) Tracing ermöglichen: ■ An einer bestimmten Stelle im Programm wird jeweils eine gewünschte Information ausgegeben (= watches), ohne das Programm anzuhalten; ■ die Möglichkeit bieten, den Wert einer Variablen während des Programmablauf dauernd zu überwachen (sogenannter watch) ■ start startDebug Debug stop stop step over step over School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 25 von 52 School of Engineering step stepinin step stepout out continue continue © A. Meier/M. Braschler/J. Zeman/K. Rege Run Debugger Anzeige von Variablen Werten ■ Einen Breakpoint (= Bullet) an/vor verdächtigen Stelle setzen; Klick in grauen Bereich ■ Es werden die aktuellen Inhalte der (z.Z. sichtbaren) Variablen angezeigt 26 von 52 ■ Debugger starten ■ Ausführung stoppt sobald der Breakpoint erreicht wird. School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 27 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 28 von 52 Wieso Test ■ Fehler passieren ■ Fehler können in den Einheiten (Klassen oder allgemein "Units") selber liegen oder im Zusammenspiel mit der Teilen ■ Dynamische Testverfahren: Programm wird mit Testdaten ausgeführt Beispiele: ■ ■ Testen Blackbox-Test (Funktionaler Test) Whitebox-Test (Struktureller Test) ■ Statische Testverfahren: Programm wird nicht ausgeführt, sondern nur der Quellcode analysiert. Beispiele: ■ ■ Inspektion/Review Walkthrough ■ Formale Methoden (Verifikation) ■ ■ School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 29 von 52 Testen ■ School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 30 von 52 Test Grundsätze ■ Grundsatz 1: Es muss immer getestet werden ■ Testen soll sicherstellen, dass das Programm ■ Dabei wird versucht, analytisch/logisch zu beweisen, dass ein Programm für alle in Frage kommenden Eingabedaten korrekt abläuft und die Spezifikationen erfüllt. Diese Methode kommt jedoch nur in seltenen Ausnahmefällen zur Anwendung (zu teuer) ■ die Spezifikation erfüllt möglichst in allen/vielen Anwendungsfällen fehlerfrei läuft. Jede Funktion - sei sie noch so trivial - muss getestet werden ■ Grundsatz 2: Richtige Resultate müssen vor dem Test bestimmt werden ■ Probleme: es lassen sich nicht alle Fälle austesten: ■ ■ ■ z.B. Multiplikation von zwei 32-Bit zahlen -> 2^64 Fälle 10^9/sec -> 586 Jahre Mit Tests kann man zwar Fehler finden aber nicht die Fehlerfreiheit garantieren Test war erfolgreich, wenn ein Fehler gefunden wurde ■ Grundsatz 3: Bei jeder Änderung müssen Tests nochmals durchgeführt werden Test Testdurch durcheine einefremde, fremde, nicht nichtwohlgesonnene wohlgesonnene Person Person ■ ■ heisst das nicht dass, das Programm ist korrekt ist Anzahl gefundener Fehler erlaubt Abschätzungen über die Zahl der noch verborgenen ■ Problem: bei jeder Fehlerkorrektur können wieder neue Fehler eingebaut werden School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege ■ Diese Testfälle sind im Mindestfall dokumentiert, damit sie bei späteren Änderungen erneut durchgeführt werden können (sog. Regressionstest). Damit wird verhindert, dass beim Ändern neue Fehler eingebaut werden, die die bisher schon laufenden Funktionen beeinträchtigen. ■ Grundsatz 4: Test Resultate müssen analysiert und protokolliert werden ■ Man kann nicht alle Fälle testen -> Wenn bei einem Test keine Fehler gefunden wurden ■ Damit verhindert man den wird-schon-richtig-sein Effekt 31 von 52 Die Resultate der Test gehören zur Dokumentation ■ Grundsatz 5: Tests müssen durch Werkzeug unterstützt werden ■ So kann gewährleistet werden, dass die Durchführung der Tests automatisiert und systematisiert wird School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 32 von 52 Inkrementelle Entwicklung, TDD ■ Man sollte nicht alle Klassen auf einmal schreiben, dann alle zusammenfügen und dann testen ■ Besser: Programm inkrementell entwickeln: ■ ■ ■ Ein Programmteil nach dem anderen entwickeln und testen Dann Programmteil zum System hinzufügen und System als Ganzes testen Dann den nächsten Teil entwickeln und hinzufügen Dynamisch Testverfahren ■ Reihenfolge beim Entwickeln und Testen: Am häufigsten Buttom-up ■ ■ Zuerst Klassen, die unabhängig von anderen Klassen sind entwickeln und testen Dann, nach und nach die darauf aufbauenden Klassen ■ Test Driven Development (TDD) zuerst die Tests und dann die Programme entwickeln School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 33 von 52 Black-Box Test 34 von 52 ■ Es wird anhand der Quellen (Inhalt bekannt) getestet ■ Testen des Programms mit ausgewählten repräsentativen Eingabedaten ■ © A. Meier/M. Braschler/J. Zeman/K. Rege White-Box Test ■ Test ohne Wissen des internen Aufbaus des Programms (nur Ein- und Ausgabe) : Blackbox ■ School of Engineering ■ Grundsätzlich beruht der WhiteBox-Test auf dem Prinzip, dass alle Elemente eines Programms durch mindestens einen Testfall geprüft werden sollen. Solche Elemente können sein: Programmzweige, Anweisungen, Bedingungen. ■ Bestimmung der Eingabewerte anhand der Logik des Programms: Randwerte, typische Werte Aufgrund der Programmspezifikationen ■ ■ ■ ■ richtige Resultate müssen vorher festgelegt sein ■ z.B. Test auf Wert im Programm -> +1/-1/0 dieses Werts Jeder Programm-Pfad/Zweig soll mindestens einmal durchlaufen werden Im Bedingungstest (if) jede wesentliche Bedingungskombination testen Fehler werden provoziert ■ Wir behandeln hier nur die einfachste Art von Strukturtest, den sogenannten Anweisungsüberdeckungstest: TestEingabewerte School of Engineering Programm © A. Meier/M. Braschler/J. Zeman/K. Rege AusgabeEingabewerte 35 von 52 ■ Ziel: Jede Anweisung sollte mindestens einmal ausgeführt werden School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 36 von 52 White-Box Test White-Box-Test, Aufgabe Aufgabe 3: Beispiel: Das Programm soll die grösste von drei Zahlen a, b, c suchen. int a, b, c ; int largest; if (a > b){ if (a > c){ largest } else{ largest } } else{ if (b > c){ largest } else{ largest } } School of Engineering Testfälle: Es genügen die folgenden 4 Datensätze, um alle Anweisungen einmal auszuführen: = a; = c; = b; = c; Testnummer Testdaten a, b, c Resultat 1 2 3 4 3, 2, 1 3, 2, 4 4, 5, 3 1, 2, 3 3 4 5 3 © A. Meier/M. Braschler/J. Zeman/K. Rege 37 von 52 Testen von Einheiten ■ Erstellen Sie einen Whitebox-Test für die folgende Klasse ■ Wie beurteilen Sie die Effektivität dieses Tests. Zeigen Sie mindestens 2 Mängel auf. class Biggest{ private int largest = 0; private boolean firstNum = true; public void nextNumber(int n){ if (firstNum){ largest = n; firstNum = false; } else { if (n > largest){ largest = n; } } } public int getLargest(){ return largest; } public void reset(){ firstNum = true; } } School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 38 von 52 Äquivalenzklassentest Die kleinste sinnvolle Testeinheit bei OOP: Klasse. ■ Möglichkeit für den Test einzelner Klassen: Æ z.B. main-Methode verwenden, um Klasse zu testen. Dies ist möglich, da die main-Methode nicht ausgeführt wird, wenn ein Objekt der Klasse erzeugt wird. ■ Die Äquivalenzklassen sind also bezüglich Ein- und Ausgabedaten ähnliche Klassen ■ Objekte, bei denen erwartet wird, dass sie sich gleichartig verhalten. Bspw. im Programm zur Verwaltung eines Fuhrparks sind äquivalente Klassen Fahrzeuge ■ ■ Beispiel: Klasse Biggest könnte durch folgende main-Methode ergänzt werden: public static void main(String[] args){ Biggest big = new Biggest(); big.nextNumber(-1); big.nextNumber(2); big.nextNumber(4); System.out.println("Numbers : -1, 2, 4 ; Biggest: " + big.getLargest()); big.reset(); big.nextNumber(9); big.nextNumber(2); System.out.println("Numbers : 9, 2 ; Biggest: " + big.getLargest()); } Bspw Ferrari - BMW nicht jedoch Ferrari - Mitarbeiter. ■ Das Wesen der Äquivalenzklassenbildung ■ ■ ■ die gesamten Eingabedaten und Ausgabedaten eines Programms in Gruppen von Äquivalenzklassen zu unterteilen so dass man annehmen kann, dass mit jedem beliebigen Objekt einer Klasse die gleichen Fehler wie mit jedem anderen Objekt dieser (Äquivalenz-)Klasse gefunden werden Bspw. Ferrari ENZO - BMW M3). Die Bildung von Testfällen zu Äquivalenzklassen folgt dieser Abfolge: ■ Test der Klasse durch Aufruf von: java Biggest School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 39 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 40 von 52 jUnit ■ jUnit: Java Framework für das automatisierte und systematische Testen ■ Fokusiert auf Unit/Einheiten Tests ■ Ziele ■ ■ Automatisiertes Testen von Einheiten ■ ■ Einfach Unit Tests zu schreiben Einfach Tests auszuführen ■ Einzeltest und alle Tests zusammen (Test Suiten) ■ Tests können automatisiert werden (no user input) Einfache Darstellung und Auswertung der Resultate ■ Green = good ■ Red = one or more tests failed Unterstützung der Überprüfung der Testresultate: Assertion-API http://www.junit.org/index.htm/ School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 41 von 52 Hello jUnit School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 42 von 52 Ein eigener jUnit Test Case (pro Klasse) public publicclass classMyClass MyClass{ { public publicString Stringhello() hello(){ { return return"Hello "HelloWorld"; World"; }} }} ■ Klasse erbt von TestCase und heisst Test<KlasseName> (Konvention) Zu Zutestende testendeKlasse Klasse Test TestKlasse Klassemuss mussvon von TestCase TestCaseerben erben ■ Konstruktur mit String Parameter, der den Basisklassen Konstruktor aufruft ■ Folgen von test<MethodeName> Methoden (Namenskonvention: "test"-Prefix) ■ import junit.framework.*; import junit.framework.*; public class TestMyClass extends TestCase { public class TestMyClass extends TestCase { Assertion auf Assertion auf richtiges Resultat richtiges Resultat Test Methode muss Test Methode muss mit "test" beginnen mit "test" beginnen ■ public TestMyClass (String name) { public TestMyClass (String name) { super(name); super(name); } } optional setUp können Initialisierung vorgenommen werden optional tearDown können Aufräumarbeiten durchgeführt werden ■ innerhalb der Test Methoden eine Reihe von asserts public void testHello() throws Exception { public void testHello() throws Exception { MyClass myClass = new MyClass(); MyClass myClass = new MyClass(); assertEquals(myClass.hello(),"Hello World"); assertEquals(myClass.hello(),"Hello World"); } } } } public public class class TestMyClass TestMyClass extends extends TestCase TestCase {{ public public TestMyClass TestMyClass (String (String name) name) {{ super(name); super(name); }} public static void main(String[] args) { public static void main(String[] args) { junit.swingui.TestRunner.run(TestMyClass.class); junit.swingui.TestRunner.run(TestMyClass.class); } } Übergabe der Test Klasse an jUnit Übergabe der Test Klasse an jUnit Framework; dieses führt dann die Tests Framework; dieses führt dann die Tests selbständig durch selbständig durch School of Engineering import import junit.framework.*; junit.framework.*; © A. Meier/M. Braschler/J. Zeman/K. Rege }} 43 von 52 School of Engineering public public void void testHello() testHello() throws throws Exception Exception {{ /* /* Asserts Asserts */ */ }} public public void void testHello2() testHello2() throws throws Exception Exception {{ /* /* Asserts Asserts */ */ } } © A. Meier/M. Braschler/J. Zeman/K. Rege 44 von 52 Assertion-Methoden der Klasse TestCase Ausführen der Tests ■ Teste ob Wert dem erwarteten entspricht ■ Einzeltest in Main der Test Klasse assertEquals(T expected, T actual); ■ assertEquals(double expected, double actual, double delta); ■ … ■ ■ Teste ob Objektreferenz Text UI – java junit.textui.TestRunner <Suite> AWT UI - java junit.awtui.TestRunner <Suite> Swing UI - java junit.swingui.TestRunner <Suite> public public static static void void main(String[] main(String[] args) args) {{ junit.swingui.TestRunner.run(TestMyClass.class); junit.swingui.TestRunner.run(TestMyClass.class); }} assertNull(Object object); assertNotNull(Object object); assertSame(Object expected,Object actual); ■ Genereller Test assertTrue(boolean condition); ■ Immer falsch fail(String message); ■ Falls in der Methode eine Exception geworfen wird, wird diese als Error protokolliert School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 45 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege TestSuiten Ausführen einer TestSuite ■ Zusammenfassen von Tests zu Test Suite (pro Package) ■ 1. Als Main Methode der Test-Suite Klasse ■ muss statische Methode suite() enthalten 46 von 52 public public static static void void main( main( String[] String[] args args )) {{ (new (new junit.swingui.TestRunner()).run(suite()); junit.swingui.TestRunner()).run(suite()); }} import import junit.framework.Test; junit.framework.Test; import import junit.framework.TestSuite; junit.framework.TestSuite; ■ 2. Auf Kommandozeile public public class class AllTests AllTests extends extends TestSuite TestSuite {{ public public static static Test Test suite() suite() {{ TestSuite mySuite = new TestSuite( TestSuite mySuite = new TestSuite( "Meine "Meine Test-Suite" Test-Suite" ); ); mySuite.addTestSuite( mySuite.addTestSuite( meinpackage.MeineKlasseTest.class meinpackage.MeineKlasseTest.class ); ); // // ... ... weitere weitere Testklassen Testklassen hinzufügen hinzufügen return return mySuite; mySuite; }} }} ■ ■ java -cp .;../classes;junit.jar junit.textui.TestRunner AllTests java -cp .;../classes;junit.jar junit.textui.TestRunner AllTests java -cp .;../classes;junit.jar junit.textui.TestRunner AllTests java -cp .;../classes;junit.jar junit.swingui.TestRunner AllTests java .;../classes;junit.jar junit.swingui.TestRunner AllTests java -cp-cp .;../classes;junit.jar junit.swingui.TestRunner AllTests ■ 3. In IDE integriert ■ 4. Als Teil des Build Scripts School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 47 von 52 School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 48 von 52 Inspektion und Walkthrough ■ Jemand (nicht der Entwickler selbst) studiert die Quellen ■ Überprüft anhand Quellen ob: ■ Statische Testverfahren ■ Kontrolle der Logik des Programms ■ Alle Variablen initialisiert werden. ■ Schlaufen auch beendet werden. ■ Methodenaufrufe die richtigen Argument haben. ■ Bedingungen korrekt formuliert sind. ■ Programm von Hand auf Papier durchspielen (Walkthrough): "make the Chinese" Überprüfung des Programmierstils ■ Sprechende Variablennamen ■ Adäquate Kommentare ■ Java Stile Richtlinien: Klassen gross, Methoden klein ■ Einrückungsregeln (besonders bei geschachtelten Konstrukten) ■ keine "magic Numbers" im Programm ■ Es gibt auch Tools dafür: z.B. FindBugs http://findbugs.sourceforge.net/ School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 49 von 52 School of Engineering ■ Es wird formal überprüft, ob des Programm korrekt ist ■ Fehlerarten ■ Resultat muss formalisierbar sein ■ ■ z.B. Array s ist sortiert ∀i : s[i ] ≤ s[i + 1] Lokalisierung/ Informationsbeschaffung ■ Dynamisches Testen ■ Idee der Vor- und Nachbedingungen: {P} s {Q} ■ ■ ■ Programm formal korrekt, wenn für alle Werte, die die (Vor-) Bedingung P erfüllen nach der Ausführung des Programmstückes s die (Nach-) Bedingung Q erfüllen: { x ≥ 0} y = sqrt(x){ y = x} © A. Meier/M. Braschler/J. Zeman/K. Rege Syntaktische Fehler Logische Fehler ■ Fehlersuche ■ School of Engineering 50 von 52 Zusammenfassung Testen Formale Programmverifikation ■ © A. Meier/M. Braschler/J. Zeman/K. Rege 51 von 52 Black-Box White-Box ■ Statisches Testen ■ ■ Inspektion Formale Programmverifikation ■ Programm inkrementell entwickeln und die Programmteile (Bausteine) separat testen School of Engineering © A. Meier/M. Braschler/J. Zeman/K. Rege 52 von 52