Software-Qualität Prof. Dr. Stephan Kleuker Hochschule Osnabrück Software-Qualität Stephan Kleuker 1 Ich • Prof. Dr. Stephan Kleuker, geboren 1967, verheiratet, 2 Kinder • seit 1.9.09 an der FH, Professur für Software-Entwicklung • vorher 4 Jahre FH Wiesbaden • davor 3 Jahre an der privaten FH Nordakademie in Elmshorn • davor 4 ½ Jahre tätig als Systemanalytiker und Systemberater in Wilhelmshaven • [email protected], Raum SI 0109 Software-Qualität Stephan Kleuker 2 Ablauf • 2h Vorlesung + 2h Praktikum = 5 CP • Praktikum : – Anwesenheit = (Übungsblatt vorliegen + Lösungsversuche zum vorherigen Aufgabenblatt) – Übungsblätter mit Punkten ( 100), zwei/drei Studis – Praktikumsteil mit 85 oder mehr Punkten bestanden • Prüfung: Projektaufgabe (Kenntnisse aus der Vorlesung anwenden und/oder eigene Untersuchungen) • Folienveranstaltungen sind schnell, bremsen Sie mit Fragen • von Studierenden wird hoher Anteil an Eigenarbeit erwartet Software-Qualität Stephan Kleuker 3 Verhaltenscodex • • • • Rechner sind zu Beginn der Veranstaltung aus Handys sind aus Wir sind pünktlich Es redet nur eine Person zur Zeit • Sie haben die Folien zur Kommentierung in der Vorlesung vorliegen, ab Mi-Abend mit Aufgaben im Netz, Aufgabenzettel liegen in der Übung vor (Ihre Aufgabe) http://www.edvsz.hs-osnabrueck.de/kleuker/index.html • Probleme sofort melden • Wer aussteigt teilt mit warum Software-Qualität Stephan Kleuker 4 Grundvoraussetzungen Ordentliche Programmierkenntnisse in Java • Klasse • Methode • Klassenvariable und Klassenmethode • Sichtbarkeit • Vererbung • abstrakte Klassen • Collections (z. B. Liste, Menge) • Polymorphie • schön: weitere Sprachen Software-Qualität Stephan Kleuker 5 Ziele • Verfahren zur Qualitätssicherung (QS) kennen lernen, anwenden lernen, situationsabhängig bewerten lernen • Prozess zur systematischen QS kennen und nutzen lernen • Umgang und selbständige Einarbeitung in Beispielwerkzeuge • Fehler kennenlernen und bei eigenen Entwicklungen vermeiden Randbedingungen • Programmierung in Java (Erkenntnisse übertragbar auf andere Sprachen) • Open Source Werkzeuge • keine Software-Ergonomie Software-Qualität Stephan Kleuker 6 Themengebiete (Planung) 1 Fehlerquellen von Software 2 Unit - Tests 3 Äquivalenzklassentests 4 Überdeckungstests 5 Vorgehensmodelle und Testen 6 Mocking 7 Test von Software mit Nutzungsoberflächen 8 Metriken 9 Konstruktive Qualitätssicherung 10 Performance und Speicherauslastung 11 Testautomatisierung 12 Organisation des QS-Prozesses in IT-Projekten 13 Ausblick: Test von Web-Projekten Software-Qualität Stephan Kleuker 7 Literatur • Stephan Kleuker, Qualitätssicherung durch Softwaretests, Springer Vieweg, Wiesbaden, 2013 • http://home.edvsz.hsosnabrueck.de/skleuker/CSI/index.html • Peter Liggesmeyer, Software-Qualität: Testen, Analysieren und Verifizieren von Software, Spektrum Akademischer Verlag, 2. Auflage, 2009 • Stephan Kleuker, Grundkurs SoftwareEngineering mit UML, Kapitel 11, Springer Vieweg, 3. Auflage, 2013 Software-Qualität Stephan Kleuker 8 1. Fehlerquellen von Software • • • • • • • Software-Fehler und ihre dramatischen Folgen Typische Entwicklungsphasen von Software Fehler in der Anforderungsanalyse Fehler im Design Fehler bei der Implementierung Fehler bei der Qualitätssicherung Fehler im Projektumfeld Software-Qualität Stephan Kleuker 9 Sammlung • An welche Fehler, die mit Software zusammenhängen erinnern Sie sich? Software-Qualität Stephan Kleuker 10 Beispiele markanter Software-Fehler (1/3) 1962 1971 1982 1982 1991 1992 1994 1993 1995 Trägerrakete Mariner 1, Selbstzerstörung nach Kursabweichung; in SW ein Komma statt eines Punktes Eole 1, Satellit zur Steuerung von Wetterballons, 71 Ballons bekommen irrtümlich Befehl zur Selbstzerstörung Größte nicht-nukleare Explosion durch Fehler in Steuerungs-SW einer russischen Gap-Pipeline Flugabwehrsystem der Fregatte Sheffield versagt; anfliegender argentinischer Flugkörper als Freund erkannt Flugabwehrsystem Patriot verfehlt Scud-Rakete; notwendiger Systemneustart nach max. 100 h vergesse Londoner Ambulance Service, neue SW führt zu Wartezeiten von bis zu drei Stunden Mondsonde Clementine, Sonde sollte einen Asteroiden ansteuern, durch einen Software-Fehler wurden die Triebwerke nach der Kurskorrektur nicht mehr abgestellt Pentium-Chip, fehlerhafte Divisionsberechnung Automatischer Gepäcktransport im Flughafen Denver funktioniert nicht; zu viele Informationen im Netz Software-Qualität Stephan Kleuker 11 Beispiele markanter Software-Fehler (2/3) 1996 1999 2001 2002 2003 2003 2004 2005 2005 2005 2005 Notwendige Selbstzerstörung der Ariane 5; falsche Steuersignale durch SW-Übernahme von Ariane 4 Fehlende Sturmwarnung für Süddeutschland, da SW starken Druckabfall als Messfehler interpretierte Fehlerhafte Bestrahlung von Krebspatienten durch nicht bedachten Bedienfehler Amerikanische Bomben schlagen wg. SW-Fehler 15 bis 250 m zu weit links ein Patriot schießt befreundetes englisches Flugzeug ab Stromausfall USA, falsche Reaktion in SW auf starke Lastschwankungen im Netz Rückruf Mercedes-Transporter, Fehler in Steuerungs-SW schalten evtl. Motor ab (auch andere Fahrzeughersteller) Arbeitslosengeld (ALG 2): Berechnungsfehler führt zur Nichtzahlung von Krankenkassenbeiträgen ALG 2: keine Auszahlung bei neun-stelliger Kontonummer T-Mobile: SMS für 80 statt 19 Cent abgerechnet Russische Trägerrakete Cryosat; Signal zum Abschalten der Triebwerke der zweiten Stufe nicht weiter geleitet Software-Qualität Stephan Kleuker 12 Beispiele markanter Software-Fehler (3/3) 2011 2011 2011 2011 2012 2012 2013 2013 2013 2014 2014 Intel den 8MByte-Bug bei SSDs der Serie 320 führt zu plötzlichem Kapazitätsverlust, auch nach ersten Firmware-Update Ausfall von 5.600 Geldautomaten für 24 Stunden in Japan Störung in der DB-SW, Geldautomaten in Stand-by-Modus, Geräte erkannten weder Tageslimits noch Kontodeckung (Australien) Defekte und Übertragungsprobleme von neuer SW führten dazu, dass in New 22 unschuldige Personen festgenommen wurden (Australien) Schaltsekunde zur Synchronisation der Uhren mit Erdrotation bereitet Servern Probleme, legt Webseiten und Datenbanken lahm Android 4.2: Massive Bugs auf allen Nexus-Geräten neue Software eines Börsenhandelsunternehmens verursacht einen Schaden von 440 Millionen US-Dollar, innerhalb von 45 Min. große Menge verschiedener Aktiensorten an- und wieder verkauft acht Zeichen - schon stürzen einige Anwendungen unter Mac OS X 10.8 ab (File:///), Schuld wohl systemweite Rechtschreibkontrolle Computerfehler im Buchungssystem von American Airlines hat tausende Passagiere stranden lassen, über Stunden keine Abfertigung Fehler der neuen Playstation 4, löscht Spielstände, zerstört Spiele Durch doppeltes „goto fail“ gravierende Sicherheitslücke bei Apple Software-Qualität Stephan Kleuker 13 Historie der Software-Entwicklung • erste Computer von Spezialisten mit sehr hohem Bildungsniveau entwickelt und programmiert • Computer und Programme waren Individualleistungen • Mit steigender Verfügbarkeit von Computern stieg auch die Anzahl der SW- und HW-Fehler • Software-Entwicklung ist hoch kreativer (künstlerischer) Prozess, der mit der Zeit in einen ingenieurwissenschaftlichen Rahmen (Software-Engineering) eingebettet wurde • Zentrale Ergebnisse: Vorgehensmodelle mit Prozessmodellen (wer macht wann, was, mit wem, mit welchen Hilfsmitteln, warum) Software-Qualität Stephan Kleuker 14 Skizze eines Vorgehensmodells Anforderungsanalyse Grobdesign Feindesign Implementierung Test und Integration Bsp.: vier Inkremente Software-Qualität Merkmal: Projekt in kleine Teilschritte zerlegt, n+1-ter Schritt kann Probleme des nten Schritts lösen Vorteile: - dynamische Reaktion auf Risiken - Teilergebnisse mit Kunden diskutierbar mögliche Nachteile: - schwierige Projektplanung - schwierige Vertragssituation - Kunde erwartet zu schnell Endergebnis Stephan Kleuker 15 Qualitätssicherung (1/2) Anforderungsanalyse Feindesign Implementierung Qualitätssicherung (QS) Grobdesign Test und Integration Software-Qualität Stephan Kleuker • QS ist eigenständiges Teilprojekt • zu jedem Projekt gehört ein QS-Plan • QS Plan: was wird wann von wem wie überprüft • QS unabhängig vom Projekt organisiert • Form der QS sehr stark projektabhängig • häufig Forderung nach Normen (z.B. ISO 9000) und QS-Standards (z.B DO 178B) 16 Qualitätssicherung (2/2) konstruktive Qualitätssicherung: • QS vor Projekt planen • Auswahl von Methoden, Werkzeugen, Prozessen, Schulungen • Normen, Guidelines und Styleguides analytische Qualitätssicherung: • manuelle Verfahren (z.B: Inspektion und Audit) zur Prüfung der Einhaltung konstruktiver Ansätze • Reviews von Teilprodukten (z.B. Anforderungen, Design) • Einsatz von Testverfahren (Äquivalenzklassen, Überdeckungen) in unterschiedlichen Testphasen (Entwicklertests, Integrationstests, Systemtests, Abnahmetests) Software-Qualität Stephan Kleuker 17 Typische Fehler der Anforderungsanalyse • Kunde wird zu wenig in Planungen eingebunden • Kunde versteht Analysedokumente der IT nicht; nickt sie trotzdem ab • Rahmenbedingungen (HW, umgebende SW) nicht geklärt • Stakeholder (in irgendeiner Form Projektbetroffene) nicht in Analyse involviert (z. B. Endnutzer vergessen) • Analyse der vom Kunden genutzten SW nicht durchgeführt; z. B. Look-and-Feel an branchenüblicher Software orientieren • Grundregel für IT-Projekte: Garbage in - Garbage out Software-Qualität Stephan Kleuker 18 Typische Fehler des Designs • keine Prüfung, ob genau die Anforderungen umgesetzt werden • Design-Entscheidungen werden nicht dokumentiert (UML: Aktivitätsdiagramme, Klassendiagramme, Zustandsdiagramme, Sequenzdiagramme) • mangelndes Design-Know-how macht resultierende Software langfristig unwartbar, nicht erweiterbar • Randbedingungen z. B. der HW nicht berücksichtigt (Performance, Kommunikationsprotokolle, ...) Software-Qualität Stephan Kleuker 19 Typische Fehler der Implementierung • Laufendes Programm – unerwünschte Funktionalität – Spezifikation war missverständlich – Überflüssige Goldrandlösung • Mögliche Alternativen werden vergessen • Programme zu komplex zum Testen (Schachteln von if, while, switch) • Programm bei Wiederverwendung/Erweiterung unwartbar (versteckte Konstanten, versteckte Abhängigkeiten) • Existierende Lösungen missachtet • Kein Gedanke an Laufzeit Software-Qualität Stephan Kleuker 20 Typische Fehler der Qualitätssicherung • alle Tests werden von den Entwicklern durchgeführt • Fehlermöglichkeiten vergessen • Testfälle können nicht nachvollzogen oder wiederholt werden; mangelnde Dokumentation der Testfälle • Anforderungen nicht konsequent in Testfälle umgesetzt • fehlende Werkzeuge zur Testautomatisierung; manuelle Tests zu zeitaufwändig • Testumgebung passt nicht zur realen Zielumgebung des Kunden • Informationen zum Performance- und Lastverhalten angeschlossener Systeme nicht berücksichtigt Software-Qualität Stephan Kleuker 21 Typische Fehler in der Projektorganisation • Kunde wird zu selten über Projektstand informiert • Kunde will vor Fertigstellung der Software „nicht belästigt“ werden • Der Umgang mit Änderungswünschen des Kunden und des Auftragnehmers ist unterspezifiziert • Auftragnehmer wird über projektrelevante Änderungen beim Kunden nicht informiert • kein Risikomanagement (beim Auftragnehmer und beim Kunden) Software-Qualität Stephan Kleuker 22 Schleichende Software-Fehler - Beispiel • kleines Unternehmen geht mit viel Know-how und neuer Individual-SW auf den Markt • SW wird bei Kunden direkt vor Ort angepasst • mit Kundenzahl wächst Zahl der Anpassungen und weiterer Kundenwünsche • dadurch, dass zentrale Daten mehrfach in Modulen gehalten werden, Datenabhängigkeiten schwer analysierbar sind und Individual-SW nicht dokumentiert ist, wird SW unwartbar • typisches Problem vieler SW-Systeme: am Anfang erfolgreich werden sie irgendwann nicht mehr weiterentwickelbar Software-Qualität Stephan Kleuker 23 Ursachen für Software-Fehler • mangelndes Verständnis von Anforderungen • Übersehen von Ablaufmöglichkeiten • Programmierfehler • zu wenig Zeit für Tests • mangelnde Qualifikation der Mitarbeiter • unpassende SW-Werkzeuge • mangelndes Managementverständnis für IT-Entwicklung • hoher Kostendruck, starker Preiskampf Software-Qualität Stephan Kleuker 24 Teufelsquadrat für IT-Projekte (Sneed) Qualität Funktionalität Kosten Zeit • Konzentration auf ein Ziel benötigt bei gleichen Rahmenbedingungen Vernachlässigung mindestens eines anderen Ziels • generelle Verbesserung nur durch Verbesserung der Rahmenbedingungen (z.B. der Prozesse) Software-Qualität Stephan Kleuker 25 Qualitätssicherung • Qualitätssicherung (allgemein) Alle geplanten und systematischen Tätigkeiten, die notwendig sind, um ein angemessenes Vertrauen zu schaffen, dass ein Produkt oder eine Dienstleistung die gegebenen Qualitätsanforderungen erfüllt • Qualitätssicherung (softwarespezifisch) Die Gesamtheit aller Maßnahmen und Hilfsmittel, die mit dem Ziel eingesetzt werden, die gestellten Anforderungen an den Entwicklungs- und Wartungsprozess sowie an das Softwareprodukt zu erreichen aus DIN 55350-11: Begriffe zu Qualitätsmanagement und Statistik, Teil 11, 8/95 Software-Qualität Stephan Kleuker 26 Aufwand für analytische Qualitätssicherung C. Jones, Applied Software Measurement, 1991 Software-Qualität Stephan Kleuker 27 Wann werden Fehler gefunden? K.-H. Möller, Ausgangsdaten für Qualitätsmetriken, 1996 Software-Qualität Stephan Kleuker 28 Prinzip der integrierten Qualitätsprüfung [Lig] Analyse Prüfungen durch Werkzeug Inspektion der Ergebnisse ok nicht ok Entwurf Prüfungen durch Werkzeug Inspektion der Ergebnisse ok nicht ok Kodierung nicht ok Software-Qualität Prüfungen durch Compiler Modul-, Integrations-, ...tests ... Stephan Kleuker 29 Definitionen (1/2) [Lig] • Qualität (DIN 55350-11) definiert als „Beschaffenheit einer Einheit bezüglich ihrer Eignung, festgelegte und abgeleitete Erfordernisse (Qualitätsanforderungen) zu erfüllen“ • Qualitätsanforderung: Gesamtheit der Einzelanforderungen an eine Einheit, die die Beschaffenheit dieser Einheit betreffen • Qualitätsmerkmal: Die konkrete Beurteilung von Qualität geschieht durch so genannte Qualitätsmerkmale. Diese stellen Eigenschaften einer Funktionseinheit dar, anhand derer ihre Qualität beschrieben und beurteilt wird, die jedoch keine Aussage über den Grad der Ausprägung enthalten. Ein Qualitätsmerkmal kann über mehrere Stufen in Teilmerkmalen verfeinert werden. Software-Qualität Stephan Kleuker 30 Definitionen (2/2) [Lig] • Qualitätsmaß: konkrete Ausprägung eines Qualitätsmerkmals geschieht durch so genannte Qualitätsmaße; dies sind Maße, die Rückschlüsse auf die Ausprägung bestimmter Qualitätsmerkmale zulassen (Beispiel: Durchschnittliche Antwortzeit für Laufzeiteffizienz) • Fehlverhalten oder Ausfall (failure) zeigt sich dynamisch bei der Benutzung eines Produkts, beim dynamischen Test einer SW erkennt man keine Fehler, sondern Fehlverhalten bzw. Ausfälle. • Fehler oder Defekt (fault, defect) ist bei SW die statisch im Programmcode vorhandene Ursache eines Fehlverhaltens oder Ausfalls Software-Qualität Stephan Kleuker 31 Qualitätsmerkmale funktional • Korrektheit nicht funktional • Sicherheit • Zuverlässigkeit • Verfügbarkeit • Robustheit • Speicher- und Laufzeiteffizienz • Änderbarkeit • Portierbarkeit • Prüfbarkeit • Benutzbarkeit Anmerkung: Qualitätsmerkmale können Wechselwirkungen haben Software-Qualität Stephan Kleuker 32 Einschub: Reguläre Ausdrücke • Reguläre Ausdrücke beschreiben eine Menge von Worten, auch Strings genannt • Typische Fragestellung: Hat ein String den gewünschten Aufbau? Der Aufbau wird mit regulären Ausdrücken beschrieben. • genauer: Theoretische Informatik, Aufbau regulärer Ausdruck • benötigt: erlaubte Zeichen, Oder-Verknüpfung, Sequenz von regulären Ausdrücken, beliebige Wiederholung eines regulären Ausdrucks, leere Menge • jede Programmiersprache bietet Bibliotheken für reguläre Ausdrücke zur Analyse und Aufteilung von Strings • https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html Software-Qualität Stephan Kleuker 33 reguläre Ausdrücke in Java (1/5) public class Main { public static void pruefe(String regulaererAusdruck, String s){ System.out.println(Pattern.matches(regulaererAusdruck, s)); } public static void main(String[] pruefe("q","q"); // pruefe("qq","q"); // pruefe("q*","q"); // pruefe("q*",""); // pruefe("q+",""); // pruefe("q+","q"); // pruefe("q|r","q"); // pruefe("q|r","r"); // pruefe("q|r","s"); // pruefe("(q|r)*","qqrrqq"); // Software-Qualität arg){ true false true // beliebig oft true false // mindestens einmal true true // Alternative (oder) true false true Stephan Kleuker 34 reguläre Ausdrücke in Java (2/5) pruefe("..","qr"); pruefe("..","qrs"); pruefe(".*","blubb"); pruefe("q?",""); pruefe("q?","q"); pruefe("q?","qq"); pruefe("q{3}","qq"); pruefe("q{3}","qqq"); pruefe("q{3,}","qq"); pruefe("q{3,}","qqq"); pruefe("q{3,5}","qq"); pruefe("q{3,5}","qqqqqq"); pruefe("q\\*","qq"); pruefe("q\\*","q*"); pruefe("q(\\*)","q*"); pruefe("q\\\\","q\\"); Software-Qualität // // // // // // // // // // // // // // // // true false true true true false false true false true false false false true true true Stephan Kleuker // ein beliebiges Zeichen // einmal oder keinmal // genau 3-mal // mindestens 3-mal // zwischen 3 und 5-mal // maskieren, Fluchtsymbol // Klammern immer erlaubt // \ in Strings immer \\ 35 reguläre Ausdrücke in Java (3/5) pruefe("[qwe]","w"); // true Alternative pruefe("[^qwe]","w"); // false //nicht eines der Zeichen pruefe("[0-9][^a-f]","1A"); // true // von - bis pruefe("[0-9][^a-f]","1a"); // false pruefe("\\d+","12345"); // true // \d Ziffer pruefe("\\D+","12345"); // false // \D keine Ziffer pruefe("\\s*"," \n\t\f\r "); // true // \s Weißraum pruefe("\\S*","hall\noo"); // false // \S kein Weißraum pruefe("\\w*","i_a"); // true // \w = [a-zA-Z_0-9] pruefe("\\W*"," \n "); // true // nicht \w pruefe("\\p{Lower}\\p{Upper}","aA"); // true // klein groß pruefe("\\p{Lower}\\p{Upper}","Aa"); //false // gibt mehr // dieser Zeichenklassen pruefe("(?i)aAa(?-i)Aa","aaaAa"); // true // (?i) Flag Case // insensitive pruefe("(?i)aAa(?-i)Aa","aaaaa"); // false // (?-i) Flag // ausschalten Software-Qualität Stephan Kleuker 36 reguläre Ausdrücke in Java (4/5) pruefe(".*","bl\nub\nb"); // false // . kein Zeilenumbruch pruefe("(?s).*","bl\nub\nb");// true // . auch für // Steuerzeichen pruefe("^a.*","aaa"); // true // ^ markiert Zeilenanfang pruefe("^a.*"," aaa"); // false pruefe(".*a$","aaa"); // true // $ markiert Zeilenende pruefe(".*a$","aaa "); // false Pattern pat = Pattern.compile("\\d+"); Matcher mat = pat.matcher("1. Die Antwort ist 42"); while(mat.find()){ System.out.println(mat.group() + " : " 1 : 0-1 + mat.start() + "-" + mat.end()); 42 : 19-21 } Software-Qualität Stephan Kleuker 37 reguläre Ausdrücke in Java (5/5) Pattern pat2 = Pattern.compile("a+"); Matcher mat2 = pat2.matcher("aaaa"); while(mat2.find()) { // greedy: Suche nach maximaler Wortlänge System.out.println(mat2.group() + " : " aaaa : 0-4 + mat2.start() + "-" + mat2.end()); } Pattern pat3 = Pattern.compile("b+?"); Matcher mat3 = pat3.matcher("bbbb"); while(mat3.find()){ // mit ? nicht greedy System.out.println(mat3.group() + " : " + mat3.start() + "-" + mat3.end()); } b b b b : : : : 0-1 1-2 2-3 3-4 H Pattern pat4 = Pattern.compile("a|o"); //[ao] ll String[] splits = pat4.split("Hallo Costa!"); C for(String s:splits){ st System.out.println(s); ! } } } Software-Qualität Stephan Kleuker 38 2. Unit - Tests • • • • • Annotationen Varianten von JUnit Testfälle mit JUnit Testsuite mit JUnit Parametrisierte Tests • Hinweis: Um im Praktikum experimentieren zu können, erst „wie schreibe ich Tests“ und danach „welche Tests sind sinnvoll“ • Warnung für dieses und die weiteren Kapitel: Der Programmcode ist meist ein Beispiel für schlechte Formatierung; hier erlaubt, um möglichst viele Details auf einer Folie mit Ihnen zu diskutieren Software-Qualität Stephan Kleuker 39 Testfall • Vor dem Testen müssen Testfälle spezifiziert werden • Vorbedingungen – Zu testende Software in klar definierte Ausgangslage bringen (z. B. Objekte mit zu testenden Methoden erzeugen) – Angeschlossene Systeme in definierten Zustand bringen – Weitere Rahmenbedingungen sichern (z. B. HW) • Ausführung – Was muss wann gemacht werden (einfachster Fall: Methodenaufruf) • Nachbedingungen – Welche Ergebnisse sollen vorliegen (einfachster Fall: Rückgabewerte) – Zustände anderer Objekte / angeschlossener Systeme Software-Qualität Stephan Kleuker 40 JUnit • Framework, um den Unit-Test eines Java-Programms zu automatisieren • einfacher Aufbau • leicht erlernbar • geht auf SUnit (Smalltalk) zurück • mittlerweile für viele Sprachen verfügbar (NUnit, CPPUnit) Software-Qualität Stephan Kleuker 41 JUnit - zwei Varianten JUnit 3.8.x • entwickelt für „klassisches“ Java vor Java 5 • Testklassen müssen von einer Klasse TestCase erben • Tests müssen in Testklassen stehen • gibt einige weitere QS-Werkzeuge, die solche Tests nutzen bzw. selbst erstellen (gehen nicht mit JUnit 4) • Ideen leichter auf XUnit-Varianten übertragbar JUnit 4.x • nutzt Annotationen • Tests können in zu testenden Klassen stehen • etwas flexibler als Junit 3.8.x JUnit 5 im Alpha-Stadium (Feb. 16) Software-Qualität Stephan Kleuker 42 Einsatz von Annotationen: Weiterverarbeitung // HelloWorldService.java import javax.jws.WebMethod; import javax.jws.WebService; @WebService public class HelloWorldService { @WebMethod public String helloWorld() { return "Hello World!"; } } • Annotationen beginnen mit einem @ und können, z. B. von anderen Programmen zur Weiterverarbeitung genutzt werden • hier z. B. soll die Erzeugung eines Web-Services durch die Kennzeichnung relevanter Teile durch Annotationen erfolgen • Java besitzt seit Version 5 Annotationen, man kann selbst weitere definieren Software-Qualität Stephan Kleuker 43 Syntax von Annotationen • Annotationen können Parameter haben • ohne Parameter: @EinfacheAnnotation • mit einem Parameter @EinAnno(par="Hallo") oder @EinAnno("Hallo") (bei nur einem Parameter kann der Parametername weggelassen werden) • mit mehreren Parametern werden Wertepaare (Parametername, Wert) angegeben @KomplexAnno(par1="Hi", par2=42, par3={41,43}) • Annotationen können bei/vor anderen Modifiern (z.B. public, abstract) bei folgenden Elementen stehen: − Exemplar- und Klassenvariable – package – class, interface, enum − lokale Variablen − Parameter – Methode • Man kann einschränken, wo welche Annotation erlaubt ist Software-Qualität Stephan Kleuker 44 Beispiel: Nutzung vordefinierter Annotation public class Oben { public void supertolleSpezialmethode(){ System.out.println("Ich bin oben"); } } public class Unten extends Oben{ @Override public void superTolleSpezialmethode(){ System.out.println("Ich bin unten"); } public static void main(String[] s){ Unten u = new Unten(); u.supertolleSpezialmethode(); } ohne Annotation in Java 1.4: } Ich bin oben Compilermeldung: ..\..\netbeans\Annotationen\src\Unten.java:2: method does not override a method from its superclass @Override public void superTolleSpezialmethode(){ 1 error Software-Qualität Stephan Kleuker 45 Konstruktiver Ansatz • Testfälle werden in Java programmiert, keine spezielle Skriptsprache notwendig • Idee ist inkrementeller Aufbau der Testfälle parallel zur Entwicklung – Pro Klasse wird mindestens eine Test-Klasse implementiert (oder in Klasse ergänzt) • JUnit als Library in NetBeans integriert • JUnit ist in Eclipse integriert • sonst muss das Paket junit.jar zu CLASSPATH hinzugefügt werden • seit JUnit 4.11 wird neben JUnit auch Hamcrest-MatcherBibliothek benötigt (vorher fest integriert) Software-Qualität Stephan Kleuker 46 Spezifikationsausschnitt • Gegeben sei eine Aufzählung mit den folgenden Werten package verwaltung.mitarbeiter; public enum Fachgebiet { ANALYSE, DESIGN, JAVA, C, TEST } • Zu entwickeln ist eine Klasse Mitarbeiter, wobei jedes Mitarbeiterobjekt – eine eindeutige Kennzeichnung (id) hat – einen änderbaren Vornamen haben kann – einen änderbaren Nachnamen mit mindestens zwei Zeichen hat – eine Informationssammlung mit maximal drei Fachgebieten hat, die ergänzt und gelöscht werden können Software-Qualität Stephan Kleuker 47 Klasse Mitarbeiter (1/4) - fast korrekt package verwaltung.mitarbeiter; import java.util.HashSet; import java.util.Set; public class Mitarbeiter { private int id; private static int idGenerator = 100; private String vorname; private String nachname; private Set<Fachgebiet> fachgebiete; public Mitarbeiter(String vorname, String nachname) { if (nachname == null || nachname.length() < 2) throw new IllegalArgumentException( "Nachname mit mindestens zwei Zeichen"); this.vorname = vorname; this.nachname = nachname; this.id = idGenerator++; this.fachgebiete = new HashSet<>(); } Software-Qualität Stephan Kleuker 48 Klasse Mitarbeiter (2/4) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getVorname() { return vorname; } public void setVorname(String vorname) { this.vorname = vorname; } public String getNachname() { return nachname; } public void setNachname(String nachname) { this.nachname = nachname;} public Set<Fachgebiet> getFachgebiete() { return fachgebiete;} public void setFachgebiete(Set<Fachgebiet> fachgebiete) { this.fachgebiete = fachgebiete; } Software-Qualität Stephan Kleuker 49 Klasse Mitarbeiter (3/4) public void addFachgebiet(Fachgebiet f){ this.fachgebiete.add(f); if(this.fachgebiete.size() > 3){ this.fachgebiete.remove(f); throw new IllegalArgumentException( "Maximal 3 Fachgebiete"); } } public void removeFachgebiet(Fachgebiet f){ this.fachgebiete.remove(f); } public boolean hatFachgebiet(Fachgebiet f){ return this.fachgebiete.contains(f); } Software-Qualität Stephan Kleuker 50 Klasse Mitarbeiter (4/4) @Override public int hashCode() { return id; } @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; Mitarbeiter other = (Mitarbeiter) obj; return (id == other.id); } @Override public String toString(){ StringBuilder erg = new StringBuilder(this.vorname + " " + this.nachname + " (" + this.id +")[ "); for(Fachgebiet f: this.fachgebiete) erg.append(f + " "); erg.append("]"); return erg.toString(); Stephan Kleuker } Software-Qualität 51 Testen von Hand public static void main(String... s){ Mitarbeiter m = new Mitarbeiter("Uwe","Mey"); m.addFachgebiet(Fachgebiet.ANALYSE); m.addFachgebiet(Fachgebiet.C); m.addFachgebiet(Fachgebiet.JAVA); System.out.println(m); m.addFachgebiet(Fachgebiet.TEST); } Uwe Mey (100)[ C JAVA ANALYSE ] Exception in thread "main" java.lang.IllegalArgumentException: Maximal 3 Fachgebiete at verwaltung.mitarbeiter.Mitarbeiter.addFachgebiet(Mitarbei ter.java:58) at verwaltung.mitarbeiter.Mitarbeiter.main(Mitarbeiter.java: 99) Software-Qualität Stephan Kleuker 52 Anlegen einer Testklasse (1/2) Software-Qualität Stephan Kleuker 53 Anlegen einer Testklasse (2/2) entweder gleiches Paket wie zu testende Klasse oder „test.“ davor erzeugt Beispiel-Code; auch weglassbar Software-Qualität Stephan Kleuker 54 Testen mit JUnit • Tests werden mit Methoden durchgeführt, die mit Annotation @Test markiert sind • Testmethoden haben typischerweise keinen Rückgabewert (nicht verboten) • Tests stehen typischerweise in eigener Klasse; für Klasse X eine Testklasse XTest (können auch in zu testender Klasse stehen) • Mit Klassenmethoden der Klasse Assert werden gewünschte Eigenschaften geprüft Assert.assertTrue("erwarteter Vorname Ute" , m.getVorname().equals("Ute")); • auch generell nutzbar: Assert.assertEquals(erwartet, gefunden), Fehlermeldung optional, guter Fehlerausgabe • JUnit steuert Testausführung, Verstöße bei Prüfungen werden protokolliert Software-Qualität Stephan Kleuker 55 Test des Konstruktors und der eindeutigen Id package verwaltung.mitarbeiter; import org.junit.Assert; import org.junit.Test; public class MitarbeiterTest { @Test public void testKonstruktor(){ Mitarbeiter m = new Mitarbeiter("Ute","Mai"); Assert.assertTrue("korrekter Vorname waere Ute" ,m.getVorname().equals("Ute")); Assert.assertTrue("korrekter Nachname waere Mai" ,m.getNachname().equals("Mai")); } @Test public void testEindeutigeId(){ Assert.assertTrue("unterschiedliche ID nicht eingehalten" , new Mitarbeiter("Ute","Mai").getId() != new Mitarbeiter("Ute","Mai").getId()); } Software-Qualität Stephan Kleuker 56 Anmerkungen • keine gute Idee: mehrere Asserts hintereinander (scheitert das Erste, wird zweites nicht betrachtet -> Tests trennen) • Assert-Methoden haben optionalen ersten String-Parameter • String kann genauere Informationen über erwartete Werte enthalten, z. B. über toString-Methoden der beteiligten Objekte • generell reicht Assert.assertTrue() aus, gibt viele weitere Methoden (auch eigene Bibliotheken) • Um nicht immer die Klasse Assert angeben zu müssen, kann man auch (persönlich unschön) folgendes nutzen: import static org.junit.Assert.*; Software-Qualität Stephan Kleuker 57 Ausschnitt: Klassenmethoden von Assert assertArrayEquals(java.lang.Object[] expecteds, java.lang.Object[] actuals) Asserts that two object arrays are equal. assertEquals(double expected, double actual, double delta) Asserts that two doubles or floats are equal to within a positive delta. assertFalse(boolean condition) assertNotNull(java.lang.Object object) assertNull(java.lang.Object object) assertSame(java.lang.Object expected, java.lang.Object actual) Asserts that two objects refer to the same object. assertThat(T actual, org.hamcrest.Matcher<T> matcher) Asserts that actual satisfies the condition specified by matcher. assertTrue(boolean condition) fail() Fails a test with no message. Software-Qualität Stephan Kleuker 58 Test-Fixture • Testfall sieht in der Regel so aus, dass eine bestimmte Konfiguration von Objekten aufgebaut wird, gegen die der Test läuft • Menge von Testobjekten wird als Test-Fixture bezeichnet • Damit fehlerhafte Testfälle nicht andere Testfälle beeinflussen können, wird die Test-Fixture für jeden Testfall neu initialisiert • In der mit @Before annotierten Methode werden Exemplarvariablen initialisiert • In der mit @After annotierten Methode werden wertvolle Testressourcen wie zum Beispiel Datenbank- oder Netzwerkverbindungen wieder freigegeben Software-Qualität Stephan Kleuker 59 Tests von hatFähigkeit public class MitarbeiterTest { private Mitarbeiter m1; @Before public void setUp() throws Exception { this.m1 = new Mitarbeiter("Uwe","Mey"); this.m1.addFachgebiet(Fachgebiet.ANALYSE); this.m1.addFachgebiet(Fachgebiet.C); this.m1.addFachgebiet(Fachgebiet.JAVA); } @Test public void testHatFaehigkeit1(){ Assert.assertTrue("vorhandene Faehigkeit C fehlt" , this.m1.hatFachgebiet(Fachgebiet.C)); } @Test public void testHatFaehigkeit2(){ Assert.assertTrue("nicht vorhandene Faehigkeit TEST gefunden" , ! this.m1.hatFachgebiet(Fachgebiet.TEST)); } Software-Qualität Stephan Kleuker 60 Testablaufsteuerung (1/4) wird einmal für alle Tests vor public class Ablaufanalyse { allen Tests ausgeführt (static !) @BeforeClass public static void setUpBeforeClass() throws Exception { System.out.println("setUpBeforeClass"); } wird einmal für alle Tests vor nach Tests ausgeführt (static !) @AfterClass public static void tearDownAfterClass() throws Exception { System.out.println("tearDownAfterClass"); } @Before public void setUp() throws Exception { System.out.println("setUp"); } Software-Qualität Stephan Kleuker 61 Testablaufsteuerung (2/4) @After public void tearDown() throws Exception { System.out.println("tearDown"); } @Test public void test1(){ System.out.println("test1"); } @Test public void test2(){ System.out.println("test2"); } } Software-Qualität Stephan Kleuker 62 Testablaufsteuerung (3/4) bei erster JUnit-Nutzung hinzugefügte Bibliotheken Software-Qualität Stephan Kleuker 63 Testablaufsteuerung (4/4) Software-Qualität Stephan Kleuker 64 Testen von Exceptions (1/3) • Erinnerung: Exception bei vierter Fähigkeit • Variante 1: Exception in @Test-Annotation festhalten @Test(expected=IllegalArgumentException.class) public void testAddFaehigkeit1(){ this.m1.addFachgebiet(Fachgebiet.TEST); } • Falls Exception nicht geworfen wird: • Vorteil: sehr kompakte Beschreibung • Nachteil: keine detaillierte Analyse der Exception möglich Software-Qualität Stephan Kleuker 65 Testen von Exceptions (2/3) • Variante 2: „klassisch“ man markiert Stellen, von denen man erwartet, dass sie nicht ausgeführt werden @Test public void testAddFaehigkeit1a(){ try{ this.m1.addFachgebiet(Fachgebiet.TEST); Assert.fail("fehlende Exception"); }catch(IllegalArgumentException e){ Assert.assertNotNull(e.getMessage()); // oder leer }catch(Exception e){ Assert.fail("unerwartet " + e); } } Software-Qualität Stephan Kleuker 66 Testen von Exceptions (3/3) • wenn keine Exception auftreten soll @Test public void testAddFaehigkeit2(){ this.m1.addFachgebiet(Fachgebiet.C); } @Test public void testAddFaehigkeit2a(){ try{ this.m1.addFachgebiet(Fachgebiet.C); }catch(Exception e){ Assert.fail("unerwartet " + e); } } Software-Qualität Stephan Kleuker 67 Ausblick: langfristige Testnutzung • Bisher geschriebene Tests werden typischerweise von Entwicklern geschrieben • Tests müssen archiviert und bei jedem Release neu ausführbar sein • Beispiel: unerfahrener Neuling ersetzt private List<Fachgebiet> fachgebiete = new ArrayList<>(); Software-Qualität Stephan Kleuker 68 Testorganisation Für große Projekte sind folgende Wünsche bzgl. der Tests typisch: a) mehrere Testklassen sollen zusammen laufen können b) man möchte Tests flexibel kombinieren können; Testwiederholung soll davon abhängen, welche Klassen betroffen sein können [schwierige Weissagung] c) man möchte Tests automatisiert ablaufen lassen können möglich z. B.: – JUnit hat Klassen zum einfachen Start von der Konsole aus – Nutzung eines cron-Jobs zu a) und b) folgende Folien: Tests zusammenfassen zu TestSuites Software-Qualität Stephan Kleuker 69 Tests zusammenfassen (1/3) - weitere Testklasse package verwaltung.mitarbeiter; import org.junit.Assert; import org.junit.Test; public class Mitarbeiter2Test { @Test public void testHinzuUndWeg(){ Mitarbeiter m = new Mitarbeiter("Hu","Go"); m.addFachgebiet(Fachgebiet.C); m.addFachgebiet(Fachgebiet.C); m.removeFachgebiet(Fachgebiet.C); Assert.assertFalse(m.hatFachgebiet(Fachgebiet.C)); } } Software-Qualität Stephan Kleuker 70 Tests zusammenfassen (2/3) - einzelne TestSuite • Anmerkung: Aufbau nicht ganz intuitiv package verwaltung.mitarbeiter; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ Mitarbeiter2Test.class , MitarbeiterTest.class}) public class MitarbeiterAllTest { // hier stehen keine Tests ! } Software-Qualität Stephan Kleuker 71 Tests zusammenfassen (3/3) - Suite in Suite package verwaltung; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import verwaltung.mitarbeiter.MitarbeiterAllTest; @RunWith(Suite.class) @SuiteClasses({MitarbeiterAllTest.class}) public class VerwaltungAllTest {} Software-Qualität Stephan Kleuker 72 Test weiterer Anforderung (1/2) • Anforderung „einen änderbaren Nachnamen mit mindestens zwei Zeichen hat“ @Test public void testAddKonstruktor2(){ try{ new Mitarbeiter(null,null); Assert.fail("fehlt Exception "); }catch(IllegalArgumentException e){ } } @Test public void testAddKonstruktor3(){ try{ new Mitarbeiter(null,"X"); Assert.fail("fehlt Exception "); }catch(IllegalArgumentException e){ } Stephan Kleuker } Software-Qualität 73 Test weiterer Anforderung (2/2) • fehlt noch: Nachnamenänderung prüfen @Test public void testSetNachname1(){ this.m1.setNachname("Mai"); Assert.assertEquals(this.m1.getNachname(), "Mai"); } @Test // hier wird ein Fehler gefunden! public void testSetNachname2(){ try{ this.m1.setNachname("X"); Assert.fail("fehlt Exception "); }catch(IllegalArgumentException e){ } } Software-Qualität Stephan Kleuker 74 Test von equals @Test public void testEquals1(){ Assert.assertTrue(this.m1.toString(), this.m1.equals(m1)); } @Test public void testEquals2(){ Assert.assertFalse(this.m1 .equals(new Mitarbeiter("Ute","Mey"))); } @Test public void testEquals3(){ Mitarbeiter m2 = new Mitarbeiter("Ufo","Hai"); m2.setId(this.m1.getId()); Assert.assertTrue(this.m1.equals(m2)); } Software-Qualität Stephan Kleuker 75 Was wann testen? • Beispiel zeigt bereits, dass man sehr viele sinnvolle Tests schreiben kann • Frage: Wieviele Tests sollen geschrieben werden? – jede Methode testen – doppelte Tests vermeiden – je kritischer eine SW, desto mehr Tests – Suche nach Kompromissen: Testkosten vs Kosten von Folgefehlern – ein Kompromiss: kein Test generierter Methoden (Konstruktoren, get, set, equals, hashCode) • (nächster Block, sinnvolle Testerstellung) Software-Qualität Stephan Kleuker 76 Tests ein/und ausschalten • verschiedene Ansätze, Stellen zu markieren, die noch entwickelt werden (//TODO) • häufiger kann man Tests schreiben, bevor Implementierung vorliegt (-> Design by Conctract durch Interfaces) • Tests können auch sonst so inaktiv geschaltet werden @Ignore("erst im Release 42.0 lauffähig") @Test public void testChefIstNummer1(){ Mitarbeiter chef = new Mitarbeiter("Ego","Ich"); Assert.assertEquals("Nr.1", chef.getId(),1); } Software-Qualität Stephan Kleuker 77 Gefahr von Endlostests • Tests laufen nacheinander, endet einer nicht, werden andere nicht ausgeführt @Test(timeout=2000) public void testGodot(){ while (!m1.getFachgebiete().isEmpty()) Assert.assertFalse(m1.hatFachgebiet(Fachgebiet.TEST)); } Software-Qualität Stephan Kleuker 78 Parametrisierte Tests • bereits gesehen: häufig wird eine Methode mit verschiedenen Parametern getestet • das Schreiben gleichartiger Tests mit ähnlichen Parametern ist langweilig und zeitaufwändig • Ansatz: Testdaten irgendwo kompakt speichern und für Testfälle einlesen • Lösung in JUnit über mit @Parameter annotierte Methode, die eine Sammlung von Daten liefert • Hinweis: gibt flexiblere Erweiterung JUnitParams https://github.com/Pragmatists/JUnitParams Software-Qualität Stephan Kleuker 79 Beispiel: Parametrisierte Tests (1/2) package verwaltung.mitarbeiter; import java.util.Arrays; import java.util.Collection; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(value = Parameterized.class) public class Mitarbeiter6Test { @Parameters public static Collection<Object[]> daten() { Object[][] testdaten = { {Fachgebiet.ANALYSE, Fachgebiet.C, Fachgebiet.C} ,{Fachgebiet.ANALYSE, Fachgebiet.C, Fachgebiet.ANALYSE} ,{Fachgebiet.C, Fachgebiet.C, Fachgebiet.C} }; return Arrays.asList(testdaten); } Software-Qualität Stephan Kleuker 80 Beispiel: Parametrisierte Tests (2/2) private Mitarbeiter m1; private Fachgebiet hat; public Mitarbeiter3Test(Fachgebiet f1, Fachgebiet f2, Fachgebiet f3) { m1 = new Mitarbeiter("Oh", "Ha"); m1.addFachgebiet(f1); m1.addFachgebiet(f2); hat = f3; } } @Test public void testHat() { Assert.assertTrue(m1.hatFachgebiet(hat)); } Software-Qualität Stephan Kleuker 81 Parametrisierung genauer angesehen public class ParametrisierterTest{ tpa1 Collection<Object[]> tpb1 tpa2 … tpan tpb2 … tpbn tpk2 … tpkn Object[] entspricht einer Parameterliste ... tpk1 publicpublic class ParametrisierterTest(par1, ParametrisierterTest(par1,par2, par2,..., ...,parn){... parn){ ... • Parameter tpa (Zeile) gehören zu einem Test und können im Test beliebig genutzt werden (Berechnung, Prüfung, …) Software-Qualität Stephan Kleuker 82 Einschränkungen bei Parametrisierung • Parameter-Methode muss Collection<Object[]> liefern @Parameters public static Collection<Object[]> data() {... • enthaltene Object[] müssen gleich groß sein • Typen der Array-Elemente müssen zum Konstruktor des Tests passen • Man kann in data() auf Dateien zugreifen • Es gibt kreative Tricks, z. B. dynamische Arraygrößen mit null-Werten zu ermöglichen Software-Qualität Stephan Kleuker 83 Parameter aus Datei testen (1/4) - Erinnerung • Einfaches Speichern in XML über Serializable • ergänzen in Mitarbeiter: import java.io.Serializable; public class Mitarbeiter implements Serializable{ private static final long serialVersionUID = 458076069326042941L; public Mitarbeiter(){ // nur für Serialisierung fachgebiete = new HashSet<Fachgebiet>(); // man bedenke Nachnamenproblem } • Anmerkung: Java kann auch mit anderen Dateien (z. B. .csv) arbeiten Software-Qualität Stephan Kleuker 84 Parameter aus Datei testen (2/4) – Daten erzeugen public class Datenerzeugung { private final String DATEI="daten.xml"; public Datenerzeugung() throws FileNotFoundException{ XMLEncoder out = new XMLEncoder(new BufferedOutputStream( new FileOutputStream(DATEI))); out.writeObject(new Mitarbeiter("Hai","Wo")); out.writeObject(Fachgebiet.TEST); out.writeObject(true); out.writeObject(Fachgebiet.TEST); out.writeObject(new Mitarbeiter("Su","Se")); out.writeObject(Fachgebiet.TEST); out.writeObject(false); out.writeObject(Fachgebiet.C); out.close(); } } public static void main(String[] args) throws FileNotFoundException { new Datenerzeugung(); } Software-Qualität Stephan Kleuker 85 Parameter aus Datei testen (3/4) – Daten lesen @Parameters public static Collection<Object[]> daten() throws FileNotFoundException { List<Object[]> ergebnis = new ArrayList<Object[]>(); XMLDecoder in = new XMLDecoder(new BufferedInputStream( new FileInputStream(DATEI))); boolean neuerWert = true; while (neuerWert) { Object[] tmp = new Object[3]; try { Mitarbeiter m = (Mitarbeiter) in.readObject(); m.addFachgebiet((Fachgebiet) in.readObject()); tmp[0] = m; tmp[1] = (Boolean) in.readObject(); tmp[2] = (Fachgebiet) in.readObject(); ergebnis.add(tmp); } catch (ArrayIndexOutOfBoundsException e) { neuerWert = false; } } in.close(); return ergebnis; Stephan Kleuker 86 } Software-Qualität Parameter aus Datei testen (4/4) – Testen public class Mitarbeiter4Test { static private final String DATEI = "daten.xml"; @Parameters ... // letzte Folie private Mitarbeiter m; private boolean erwartet; private Fachgebiet pruef; public Mitarbeiter4Test(Mitarbeiter m, Boolean b, Fachgebiet f) { System.out.println(m + " : " + b + " : " + f); this.m = m; this.erwartet = b; Hai Wo (100)[ TEST ] : true : TEST this.pruef = f; Su Se (101)[ TEST ] : false : C } } @Test public void testHat() { Assert.assertTrue(m.hatFachgebiet(pruef)==erwartet); } Software-Qualität Stephan Kleuker 87 Testauswahl mit Kategorien (1/3) • Einfache Interfaces werden als Markierung von Kategorien genutzt (spart weitere Schlüsselworte) public interface Basic { /* markiert Kategorie */ } public interface Inkrement2 { /* markiert Kategorie */ } • man kann Test-Klassen markieren @Category({Basic.class, Inkrement2.class}) public class BTest { } @Test public void testb() { System.out.println("b"); } Software-Qualität Stephan Kleuker 88 Testauswahl mit Kategorien (2/3) • man kann einzelne Tests markieren package test; import org.junit.Test; import org.junit.experimental.categories.Category; public class ATest { @Test public void testa1() { System.out.println("a1"); } } @Category(Basic.class) @Test public void testa2() { System.out.println("a2"); } Software-Qualität Stephan Kleuker 89 Testauswahl mit Kategorien (3/3) • man kann Ausführung variieren @RunWith(Categories.class) @IncludeCategory(Basic.class) @SuiteClasses({ ATest.class, BTest.class }) public class BasicTestSuite { } @RunWith(Categories.class) @IncludeCategory(Basic.class) @ExcludeCategory(Inkrement2.class) @Suite.SuiteClasses({ ATest.class, BTest.class }) public class OnlyBasicTestSuite { } Software-Qualität Stephan Kleuker a2 b a2 90 Rules • Teststeuerung kann durch Regeln angepasst werden • Regeln sind Objekte, die bestimmte Funktionalität anbieten, die durch ihre Nutzung Abläufe vereinfachen • typisch: Zusammenfassung von Aktionen @Before und @After mit optional weiterer Funktionalität • JUnit beinhaltet schon einige Regeln selbst • Entwickler können eigene Regeln schreiben (Interface beachten) • Ergänzungsbibliotheken bieten zusätzliche Regeln an • Annotation @Rule für Exemplarvariablen • Annotation @ClassRule für Klassen nutzbar (-> static, @BeforeClass, @AfterClass) Software-Qualität Stephan Kleuker 91 Beispiel-Regel: Nutzung temporärer Dateien @Test // alte Variante public void mitDatebearbeitungTest() throws IOException { File verzeichnis = new File("subfolder"); verzeichnis.mkdir(); File datei= new File("subfolder/myfile.txt"); datei.createNewFile(); Assert.assertTrue(datei.exists()); // ... muss alles von Hand aufgeräumt werden } @Rule // neue Variante public TemporaryFolder folder = new TemporaryFolder(); @Test public void testUsingTempFolder() throws IOException { File verzeichnis = this.folder.newFolder("subfolder"); File datei = this.folder.newFile("subfolder/myfile.txt"); Assert.assertTrue(datei.exists()); // ... } Software-Qualität Stephan Kleuker 92 Beispiel-Regel: Angabe erwarteter Exception @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void nichtDurchNullTest() { this.thrown.expect(ArithmeticException.class); this.thrown.expectMessage("by zero"); // Teiltext int bumm = 7 / (7-7); } @Test public void dateiErstAnlegenTest() throws Exception{ this.thrown.expect(FileNotFoundException.class); File datei= new File("hallo.txt"); datei.delete(); FileInputStream in = new FileInputStream(datei); } Software-Qualität Stephan Kleuker 93 Eigene minimale Regel (1/2): Deklaration public class MeineRegel implements TestRule{ @Override public Statement apply(Statement base, Description desc) { System.out.println(desc); return new Statement(){ @Override public void evaluate() throws Throwable { System.out.println("folge meinen Regeln"); base.evaluate(); // eigentlichen Test ausfuehren System.out.println("folgtet meinen Regeln"); } }; } } Software-Qualität Stephan Kleuker 94 Eigene minimale Regel (2/2): Beispielnutzung public class RuleAnalyseTest { @Rule public MeineRegel mr = new MeineRegel(); @Test public void test1(){ System.out.println("in test1"); } @Test public void test2(){ System.out.println("in test2"); } } Software-Qualität Stephan Kleuker test1(test.RuleAnalyseTest) folge meinen Regeln in test1 folgtet meinen Regeln test2(test.RuleAnalyseTest) folge meinen Regeln in test2 folgtet meinen Regeln 95 JUnit - Erweiterungen • Beispiel: Klasse Runner zur Teststeuerung • Aus: http://junit.sourceforge.net/javadoc/org/junit/runner/Runner.html „A Runner runs tests and notifies a RunNotifier of significant events as it does so. You will need to subclass Runner when using RunWith to invoke a custom runner. When creating a custom runner, in addition to implementing the abstract methods here you must also provide a constructor that takes as an argument the Class containing the tests. The default runner implementation guarantees that the instances of the test case class will be constructed immediately before running the test and that the runner will retain no reference to the test case instances, generally making them available for garbage collection.“ • Fehlt noch: Möglichkeit, Tests nur durchzuführen, wenn andere Tests vorher erfolgreich waren (erst Login testen, dann Funktionalität) Variante TestNG Software-Qualität Stephan Kleuker 96