1 / 41

Software-Engineering II

Software-Engineering II. Objektorientiertes Testen. Themenübersicht. Objektorientierung Aspektorientierung Vorgehensmodelle UML Analyse- & Entwurfsmuster Objektorientiertes Testen Versionsverwaltung Refactoring Labor (Praktischer Teil). Unit-Testing mit JUnit.

chi
Télécharger la présentation

Software-Engineering II

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Software-Engineering II Objektorientiertes Testen

  2. Themenübersicht Objektorientierung Aspektorientierung Vorgehensmodelle UML Analyse- & Entwurfsmuster Objektorientiertes Testen Versionsverwaltung Refactoring Labor (Praktischer Teil)

  3. Unit-Testing mit JUnit Pragmatic Unit Testing in Java with JUnit Andrew Hunt, David Thomas 176 Seiten ISBN: 0-974-5140-12 (Englisch)

  4. Testen ist ein wichtiger Bestandteil des Software-Lifecycles Testen objektorienter Software bringt verglichen mit funktionaler Programmierung einige Erschwernisse mit sich Objektorientiertes Testen

  5. Probleme bei der Objektorientierung I • Nachdem die objektorientierte Software-Entwicklung begann, sich durchzusetzen, blieb objektorientiertes Testen zunächst unbeachtet • Es gab sogar Meinungen, dass Objektorientierung das Testen von Software vereinfacht: „... the use of object-oriented design doesn’t change any basic testing principles; what does change is the granularity of the units tested.“ (Grady Booch) „Both testing and maintenance are simplified by an object-oriented approach...“ (James Rumbaugh)

  6. Im Allgemeinen die selben Anforderungen wie bei funktionaler Programmierung Generierung von authentischen Testdaten Validierung der Testergebnisse Das Programm gilt als korrekt, wenn der Ist-Zustand dem spezifizierten Soll-Zustand entspricht Probleme bei der Objektorientierung II

  7. Methoden können in objektorientierten Programmen abhängig vom Zustand der Klasse sein, welcher durch ihre Attribute geprägt wird Ein Aufruf einer Methode kann je nach Vorgeschichte der Klasse ein beliebig differenziertes Ergebnis liefern Eine Klasse zu 100% zu testen führt zu einer exponentiellen Steigerung der Testfälle Es würde bei den meisten komplexen Programm-Strukturen den finanziellen Rahmen des Projekts sprengen Unzählige Zustände

  8. Klassen sollten generisch und für eine große Anzahl von Anwendungsfällen geschrieben sein Beim Testen nicht vorhersehbar, wie eine Klasse benutzt wird Prognose der Fälle erforderlich Durch Vererbung entstehen neue Abhängigkeiten die wiederum getestet werden müssen Werden Fehler in Basisklassen nicht erkennt, erben alle Subklassen die Fehler von der Basisklasse Generik und Vererbung

  9. Nach dem Definieren von Testfällen kann automatisiert die ganze Testsuite getestet werden Vorteile: Fehler, die durch Abhängigkeiten unbemerkt entstanden sind, können einfach gefunden werden Schnelle Möglichkeit um ein System komplett auf Funktionalität zu prüfen Verringert die Zeit für das Debuggen Gibt Vertrauen in den Quellcode Verbessert das Design, da schon zu Beginn Gedanken über die Verwendung (Testbarkeit) des Codes gemacht werden müssen Voraussetzung: Gute Code-Abdeckung der Tests Automatisierte Tests

  10. Teste alles, was schief gehen kann Teste alles, was schief ging (Bugs) Neuer Quellcode ist solange fehlerhaft bis man das Gegenteil bewiesen hat Schreibe mindestens so viel Testcode wie Programmcode Führe Tests lokal bei jedem Compilieren aus Führe alle Tests vor dem Check-In in das Repository aus Generelle Grundsätze

  11. Regel: „Use your Right-Bicep“ R ight B oundary I nverse C ross-Check E rror-Conditions P erformance Was soll getestet werden?

  12. Die einfachste Kondition Teste, ob das Ergebnis richtig ist R-BICEP - Right int[] numbers = { 3, 1, 4, 2 }; Assert.assertEquals( 4, MaxValue( numbers ) );

  13. Mit Grenzwerten testen Sonderzeichen, Umlaute Leere oder fehlende Werte, bspw. null, Leerstring, 0 Unerwartete Werte, bspw. 10,000 als Alter Duplikate in Listen, die keine haben dürften Sortierte und unsortierte Werte, bpsw. für Sortieralgorithmen Befehle nicht in erwarteter Reihenfolge ausführen R-BICEP - Boundary

  14. Einige Methoden können durch die Invers-Methode verifiziert werden Definitionsbereiche beachten R-BICEP – Inverse Relationship double x = MyMath.Sqrt( 4.0 );Assert.assertTrue( Math.abs( 4.0 - x*x ) < 0.0001 );

  15. Manche Ergebnisse können mit alternativen Implementierungen geprüft werden R-BICEP - Cross-Check double x = MyMath.Sqrt( 100 );double y = Math.Sqrt( 100 ); Assert.assertTrue( Math.abs( x – y ) < 0.0001);

  16. Mit ungültigen Eingaben testen Exceptions provizieren und erwarten Umgebungsprobleme testen Speicherüberlauf Festplatte voll Systemuhrzeit inkonsistent (manipuliert) Dauerhafter oder temporärer Netzwerk-Abbruch Unzureichende Benutzer-Rechte Systemauslastung Eingeschränkte oder sehr hohe Bildschirmauflösung R-BICEP – Error-Conditions

  17. Bei manchen Methoden sind Performance-Analysen möglich R-BICEP- Performance long start = System.currentTimeMillis(); this.sortList(); long end = System.currentTimeMillis(); Assert.assertTrue( ( end - start ) < 1.0 );

  18. A utomatic T horough R epeatable I ndependant P rofessional Gute Tests

  19. Unit-Tests müssen automatisch ablaufen können Starten der Tests Prüfen der Ergebnisse Es darf kein manueller Schritt notwendig sein Datenbank-Zeilen einfügen Dateien erstellen … A TRIP - Automatic

  20. Teste alles, bei dem es wahrscheinlich ist, dass es schief geht Teste zumindest die wichtigsten Eigenschaften Grenzwerte Fehlende und ungültige Werte Die Konsequenz, mit der getestet wird, muss für jedes Projekt neu entschieden werden A TRIP - Thorough

  21. Tests sollten immer wieder ausführbar sein in jeder Reihenfolge ausführbar sein immer die gleichen Ergebnisse liefern A TRIP - Repeatable

  22. Tests sollten Klein und sauber sein Unabhängig von der Umgebung sein Unabhängig von einander sein Teste immer nur eine Sache zu einer Zeit A TRIP - Independant

  23. Tests sind richtiger Quellcode Sie müssen deshalb nach den selben professionellen Standards wie der Programmcode geschrieben sein gewartet werden A TRIP - Professional

  24. Dependency Injection 1 • Klassen mit direkten Abhängigkeiten sind schwer testbar • Sie können nicht einzeln, sondern nur als System getestet werden Zu testende Klasse

  25. Dependency Injection 2 • Die abhängige Klasse bekommt eine Referenz auf ihre Delegate-Klasse gesetzt • Der Typ der Referenz ist dabei nicht die Klasse selbst sondern ein Interface* Zu testende Klasse

  26. Dependency Injection 3 • Im Test-Code kann nun der Klasse ein Delegate gesetzt werden, welches dasselbe Interface implementiert, sich jedoch nach von der Test-Klasse vorgegebenen Regeln verhält • Diese Alternativ-Klassen nennt man Mock-Klassen (Mock = Attrappe)

  27. [Frameworks] Mockito • Mocking-Framework • Stellt Mocks zur Verfügung Class2 delegate = Mockito.mock(Class2.class); Mockito.when(delegate.foo(10)).thenReturn(“String“);new Class1().setDependancy(delegate); Mit verify kann später geprüft werden, ob ein Aufruf stattfand: Mockito.verify(delegate).foo(10);

  28. [Frameworks] Spring (I) • Dependancy Injection Framework @Resource-Annotation class Class1{ @Resource private iClass2 dependancy; } • Dependancies sind konfigurierbar ... <bean id="dependancy" class="Class2“ /> ...

  29. [Frameworks] Spring (II) • Komplexe Instanziierungen möglich ... <bean id="class1" class="Class1"> <property name="dependancy" ref=“iClass2" /> </bean> <bean id=“iClass2" class="Class2"> <constructor-arg value="Hallo Welt" /> </bean> ... Entspricht: iClass2 class2 = new Class2("Hallo Welt"); Class1 class1 = new Class1(); Class1.setDependancy(class2); Vorteil: Variable Parameter und Abhängigkeiten sind konfigurierbar! (=austauschbar)

  30. Dependency Injection 4 • OOP Design Pattern • Einsatzgebiete nicht nur auf OOP Testing beschränkt • Immer dann ideal, wenn direkte Abhängigkeiten vermieden werden sollen

  31. In Eclipse und NetBeans Ohne Eclipse: http://www.junit.org/ In Eclipse das JUnit-Library einbinden Projekt-Properties Java Build Path Libraries Werkzeug JUnit

  32. Neue (Test-)Klasse erstellen vorzugsweise in einem separaten Package für unit-tests Neue Methode erstellen, die einen Test durchführt Methode mit der Annotation @org.junit.Test versehen Package Explorer: Rechtsklick auf Klasse Run as JUnit test Testfall erstellen

  33. Aufbau eines Tests package unittest.test; publicclass SimpleTest { @org.junit.Test publicvoid testcase() { // … } } Testklasse Test-Methode Test-Code

  34. Die Klasse org.junit.Assert bietet statische Funktionen, um Tests durchzuführen Assert.assertTrue( boolean ) Test bestanden, wenn der übergebene Wert true ist Assert.assertFalse( boolean ) Test bestanden, wenn der übergebene Wert false ist Assert.assertEquals( Object, Object ) Test bestanden, wenn beide übergebene Objekte „gleich“ sind (Bsp.: Strings mit gleichem Inhalt) Assert.assertSame( Object, Object ) Test bestanden, wenn es sich bei den übergebenen Objekten und die selbe Instanz handelt Assert.assertNotNull( Object ) Test bestanden, wenn das übergebene Objekt nicht null ist Assertions

  35. Beispiel-Testfall package unittest.test; import org.junit.Assert; publicclass SimpleTest { @org.junit.Test publicvoid testcase() { Assert.assertTrue( Math.PI > 3 ); // will succeed } @org.junit.Test publicvoid badTestcase() { Assert.assertTrue( Math.PI < 3 ); // bound to fail } }

  36. Ausführen des Tests Start des Testvorgangs

  37. Test-Ergebnisse Indiziert, dass alle Testfälle erfolgreich waren Ein Testfall ist fehlgeschlagen

  38. Benötigen alle Tests einer Testklasse gemeinsame Ressourcen, können diese in einer setUp-Methode alloziert werden Dazu wird die Methode mit der Annotation @org.junit.Before versehen Ressourcen, die in der setUp-Methode alloziert werden, können in der tearDown-Methode wieder freigegeben werden Die Methode wird mit der Annotation @org.junit.After versehen Die beiden Methoden werden vor und nach jedem Test ausgeführt setUp & tearDown @org.junit.Before public void setUp(){ this.dbconnection.open(); } @org.junit.After public void tearDown() { this.dbconnection.close(); } @org.junit.Test public void testcase1() { this.dbconnection.…; } @org.junit.Test public void testcase2() { this.dbconnection.…; }

  39. Einfachere Annotations • Durch den Import von org.junit.* bzw. den benötigten Elementen kann man auf die Paketnamen verzichten import org.junit.*; class MyTest { @Before public void setUp(){ … } @Test public void testcase1() { … } }

  40. Exceptions, die während des Tests auftreten werden von JUnit abgefangen und bei der Auswertung als Fehler ausgewiesen Möchte man explizit testen, ob eine spezielle Exception geworfen wird, kann man das durch die Erweiterung der Annotation erreichen: Exceptions @Test(expected= [Exception-Name].class) Beispiel: @Test(expected= IndexOutOfBoundsException.class) publicvoid testException() { new ArrayList<Object>().get(0); // wirft Exception }

  41. Analysieren, welche Code-Stellen durch Unit-Tests bereits abgedeckt sind Einfaches Auffinden von ungetestetem Code „Nur getesteter Code kann als funktionsfähig angesehen werden!“ Beispiel: Clover [Tools] Code-Coverage

More Related