2. Unit - Tests • • • • • 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 Annotationen Varianten von JUnit Testfälle mit JUnit Testsuite mit JUnit Parametrisierte Tests • 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 32 JUnit Stephan Kleuker 33 JUnit - zwei Varianten • Framework, um den Unit-Test eines Java-Programms zu automatisieren • einfacher Aufbau • leicht erlernbar 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 • geht auf SUnit (Smalltalk) zurück • mittlerweile für viele Sprachen verfügbar (NUnit, CPPUnit) Software-Qualität Software-Qualität Stephan Kleuker 34 Software-Qualität Stephan Kleuker 35 Einsatz von Annotationen: Weiterverarbeitung Syntax von Annotationen // HelloWorldService.java import javax.jws.WebMethod; import javax.jws.WebService; @WebService public class HelloWorldService { @WebMethod public String helloWorld() { return "Hello World!"; } } • 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, woher par kommt, sehen wir später) • mit mehreren Parametern werden Wertepaare (Parametername, Wert) angegeben @KomplexAnno(par1="Hi", par2=42, par3={41,43}) • 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 36 Beispiel: Nutzung vordefinierter Annotation • Annotationen können bei/vor anderen Modifiern (z.B. public, abstract) bei folgenden Elementen stehen: – package − Exemplar- und Klassenvariable – class, interface, enum − lokale Variablen – Methode − Parameter • Man kann einschränken, wo welche Annotation erlaubt ist Software-Qualität Stephan Kleuker 37 Konstruktiver Ansatz 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 • 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 ist in Eclipse integriert, sonst muss das Paket junit.jar zu CLASSPATH hinzugefügt werden. 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 38 Software-Qualität Stephan Kleuker 39 Spezifikationsausschnitt 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; • 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 vier Fachgebieten hat, die ergänzt und gelöscht werden können Software-Qualität Stephan Kleuker 40 Klasse Mitarbeiter (2/4) 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<Fachgebiet>(); } Software-Qualität Stephan Kleuker Klasse Mitarbeiter (3/4) public int getId() { return id; } public void addFachgebiet(Fachgebiet f){ fachgebiete.add(f); if(fachgebiete.size()>3){ fachgebiete.remove(f); throw new IllegalArgumentException( "Maximal 3 Fachgebiete"); } } 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 removeFachgebiet(Fachgebiet f){ fachgebiete.remove(f); } public void setFachgebiete(Set<Fachgebiet> fachgebiete) { this.fachgebiete = fachgebiete; } public boolean hatFachgebiet(Fachgebiet f){ return fachgebiete.contains(f); } Software-Qualität 41 Stephan Kleuker 42 Software-Qualität Stephan Kleuker 43 Klasse Mitarbeiter (4/4) Testen von Hand @Override public int hashCode() { return id; } 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) @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(){ StringBuffer erg= new StringBuffer(vorname+ " "+nachname+" ("+id+")[ "); for(Fachgebiet f:fachgebiete) erg.append(f+" "); erg.append("]"); return erg.toString(); Stephan Kleuker } Software-Qualität 44 Anlegen einer Testklasse (1/2) Software-Qualität Stephan Kleuker Software-Qualität Stephan Kleuker 45 Anlegen einer Testklasse (2/2) 46 Software-Qualität Stephan Kleuker 47 Testen mit JUnit Test des Konstruktors und der eindeutigen Id • 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("korrekter Vorname" ,m.getVorname().equals("Ute")); • JUnit steuert Testausführung, Verstöße bei Prüfungen werden protokolliert Software-Qualität Stephan Kleuker 48 Anmerkungen @Test public void testEindeutigeId(){ Assert.assertTrue("unterschiedliche ID" , new Mitarbeiter("Ute","Mai").getId() != new Mitarbeiter("Ute","Mai").getId()); } Software-Qualität Stephan Kleuker 49 Ausschnitt: Klassenmethoden von Assert • keine gute Idee: mehrere Asserts hintereinander (scheitert das Erste, wird Zweite 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 • 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 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" ,m.getVorname().equals("Ute")); Assert.assertTrue("korrekter Nachname" ,m.getNachname().equals("Mai")); } Stephan Kleuker 50 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 51 Test-Fixture Tests von hatFähigkeit • 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 52 Testen von Exceptions (1/3) @Test public void testHatFaehigkeit1(){ Assert.assertTrue("vorhandene Faehigkeit" ,m1.hatFachgebiet(Fachgebiet.C)); } @Test public void testHatFaehigkeit2(){ Assert.assertTrue("nicht vorhandene Faehigkeit" ,!m1.hatFachgebiet(Fachgebiet.TEST)); } Software-Qualität Stephan Kleuker 53 • Variante 2: „klassisch“ man markiert Stellen, von denen man erwartet, dass sie nicht ausgeführt werden @Test public void testAddFaehigkeit1a(){ try{ m1.addFachgebiet(Fachgebiet.TEST); Assert.fail("fehlende Exception"); }catch(IllegalArgumentException e){ Assert.assertNotNull(e.getMessage()); }catch(Exception e){ Assert.fail("unerwartet "+e); } } @Test(expected=IllegalArgumentException.class) public void testAddFaehigkeit1(){ m1.addFachgebiet(Fachgebiet.TEST); } • Falls Exception nicht geworfen wird: • Vorteil: sehr kompakte Beschreibung • Nachteil: keine detaillierte Analyse der Exception möglich Stephan Kleuker @Before public void setUp() throws Exception { m1 = new Mitarbeiter("Uwe","Mey"); m1.addFachgebiet(Fachgebiet.ANALYSE); m1.addFachgebiet(Fachgebiet.C); m1.addFachgebiet(Fachgebiet.JAVA); } Testen von Exceptions (2/3) • Erinnerung: Exception bei vierter Fähigkeit • Variante 1: Exception in @Test-Annotation festhalten Software-Qualität public class MitarbeiterTest { Mitarbeiter m1; 54 Software-Qualität Stephan Kleuker 55 Testen von Exceptions (3/3) 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 • wenn keine Exception auftreten soll @Test public void testAddFaehigkeit2(){ m1.addFachgebiet(Fachgebiet.C); } private List<Fachgebiet> fachgebiete = new ArrayList<Fachgebiet>(); @Test public void testAddFaehigkeit2a(){ try{ m1.addFachgebiet(Fachgebiet.C); }catch(Exception e){ Assert.fail("unerwartet "+e); } } Software-Qualität Stephan Kleuker 56 Test weiterer Anforderung (1/2) Stephan Kleuker 57 Test weiterer Anforderung (2/2) • Anforderung „einen änderbaren Nachnamen mit mindestens zwei Zeichen hat“ • fehlt noch: Nachnamenänderung prüfen @Test public void testSetNachname1(){ m1.setNachname("Mai"); Assert.assertEquals(m1.getNachname(), "Mai"); } @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 Software-Qualität @Test public void testSetNachname2(){ try{ m1.setNachname("X"); Assert.fail("fehlt Exception "); }catch(IllegalArgumentException e){ } } 58 Software-Qualität Stephan Kleuker 59 Test von equals Was wann testen? @Test public void testEquals1(){ Assert.assertTrue(m1.toString(),m1.equals(m1)); } • 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) @Test public void testEquals2(){ Assert.assertFalse(m1 .equals(new Mitarbeiter("Ute","Mey"))); } @Test public void testEquals3(){ Mitarbeiter m2 = new Mitarbeiter("Ufo","Hai"); m2.setId(m1.getId()); Assert.assertTrue(m1.equals(m2)); } Software-Qualität Stephan Kleuker • (nächste Veranstaltungen, sinnvolle Testerstellung) 60 Tests ein/und ausschalten @Ignore("im nächsten Release lauffähig") @Test public void testChefIstNummer1(){ Mitarbeiter chef= new Mitarbeiter("Ego","Ich"); Assert.assertEquals("Nr.1", chef.getId(),1); } Stephan Kleuker Stephan Kleuker 61 Gefahr von Endlostests • verschiedene Ansätze, Stellen zu markieren, die noch entwickelt werden (//TODO) • häufiger kann man Tests schreiben, bevor Implementierung vorliegt (-> Design by Concept durch Interfaces) • Tests können auch sonst so inaktiv geschaltet werden Software-Qualität Software-Qualität 62 • 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 63 Testablaufsteuerung (1/3) Testablaufsteuerung (2/3) public class AblaufAnalyse { @BeforeClass public static void setUpBeforeClass() throws Exception { System.out.println("setUpBeforeClass"); } @After public void tearDown() throws Exception { System.out.println("tearDown"); } @Test public void test1(){ System.out.println("test1"); } @AfterClass public static void tearDownAfterClass() throws Exception { System.out.println("tearDownAfterClass"); } @Test public void test2(){ System.out.println("test2"); } @Before public void setUp() throws Exception { System.out.println("setUp"); } Software-Qualität Stephan Kleuker } 64 Testablaufsteuerung (3/3) Stephan Kleuker Stephan Kleuker 65 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 setUpBeforeClass setUp test1 tearDown setUp test2 tearDown tearDownAfterClass Software-Qualität Software-Qualität 66 Software-Qualität Stephan Kleuker 67 Tests zusammenfassen (1/3) - weitere Testklasse Tests zusammenfassen (2/3) - einzelne TestSuite package verwaltung.mitarbeiter; • Anmerkung: Aufbau nicht ganz intuitiv package verwaltung.mitarbeiter; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses;; 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)); } @RunWith(Suite.class) @SuiteClasses({Mitarbeiter2Test.class, MitarbeiterTest.class}) public class MitarbeiterAllTest { // hier stehen keine Tests ! } } Software-Qualität Stephan Kleuker 68 Tests zusammenfassen (3/3) - Suite in Suite Software-Qualität Stephan Kleuker 69 Parametrisierte Tests package verwaltung; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; • 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 import verwaltung.mitarbeiter.MitarbeiterAllTest; @RunWith(Suite.class) @SuiteClasses({MitarbeiterAllTest.class}) public class VerwaltungAllTest {} • Lösung in JUnit über mit @Parameter annotierte Methode, die eine Sammlung von Daten liefert Software-Qualität Stephan Kleuker 70 Software-Qualität Stephan Kleuker 71 Beispiel: Parametrisierte Tests (1/2) Beispiel: Parametrisierte Tests (2/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; 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; } @RunWith(value = Parameterized.class) public class Mitarbeiter3Test { @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 72 Einschränkungen bei Parametrisierung @Test public void testHat() { Assert.assertTrue(m1.hatFachgebiet(hat)); } } Software-Qualität Stephan Kleuker 73 Parameter aus Datei testen (1/4) - Erinnerung • Parameter-Methode muss Collection<Object[]> liefern • Einfaches Speichern in XML über Serializable • ergänzen in Mitarbeiter: @Parameters public static Collection<Object[]> data() {... • enthaltene Object[] müssen gleich groß sein • Typen der Array-Elemente müssen zum Konstruktor des Tests passen import java.io.Serializable; public class Mitarbeiter implements Serializable{ private static final long serialVersionUID = 458076069326042941L; • Man kann in data() auf Dateien zugreifen • Es gibt kreative Tricks, z. B. dynamische Arraygrößen mit null-Werten zu ermöglichen 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 74 Software-Qualität Stephan Kleuker 75 Parameter aus Datei testen (2/4) – Daten erzeugen Parameter aus Datei testen (3/4) – Daten lesen 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 76 Parameter aus Datei testen (4/4) – Testen @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; } Software-Qualität Stephan Kleuker 77 JUnit - Erweiterungen public class Mitarbeiter4Test { static private final String DATEI = "daten.xml"; @Parameters ... // letzte Folie • 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.“ 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 } • Fehlt noch: Möglichkeit, Tests nur durchzuführen, wenn andere Tests vorher erfolgreich waren (erst Login testen, dann Funktionalität) Variante TestNG @Test public void testHat() { Assert.assertTrue(m.hatFachgebiet(pruef)==erwartet); } } Software-Qualität Stephan Kleuker 78 Software-Qualität Stephan Kleuker 79