Testen, Fehlersuche, Debuggen, Loggen

Transcription

Testen, Fehlersuche, Debuggen, Loggen
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