Programmieren 2 04 Testen und Testautomatisierung Bachelor Medieninformatik Sommersemester 2016 Dipl.-Inform. Ilse Schmiedecke schmiedecke@beuth-hochschule.de 1
Das Programm funktioniert super!...denkt jeder Programmierer, der etwas fertiggestellt hat Wenn er selbst nicht ordentlich testet, finden andere die Fehler im ungünstigsten Fall der Kunde im schlimmsten Fall die Kriminologen und Gerichtsmediziner 2
Testen wozu? Programmieren erfordert Sorgfalt! Auch winzige Fehler haben Folgen Beispiel Mariner 1 Fehler werden oft erst später entdeckt Beispiel Ariane 5 Was leistet Testen? Fehlerhaftigkeit des Codes beweisen Korrektheit des Codes beweisen? à NEIN aber die Abwesenheit vorhersehbarer Schwachstellen nachweisen 3
Mariner 1 Venussonde Mariner 1 (1962) nach 290 sec. wegen Flugbahnabweichung kontrolliert zerstört FORTRAN-Code: DO 5 K = 1. 3 statt DO 5 K 1, 3 àdo5k = 1.3 nutzlose Variablenzuweisung statt Schleife!! Beispiel aus Vingenshow, Objektorientiertes Testen 4
Ariane 5 Bewährte Trägerrakete Ariane 5 (1996) deutlich schubstärker als ihre Vorgänger Fehlerhafte Flugbahnkorrektur um 20 führte zur Sprengung nach 30 sec ADA-Code: Viele Jahre lang zuverlässig funktioniert à keine neue Testreihe Erhöhte Schubstärke führte zum Zahlenüberlauf, der nicht abgefangen wurde Beispiel aus Vingenshow, Objektorientiertes Testen 5 5
Test-Propheten Testen erfordert Phantasie und Erfahrung "kann nicht passieren" ist eine Tabu-Aussage! Es gibt das "Gespür" des Testers aber darauf kann man sich nicht verlassen! bei einer Probefahrt fallen auch nicht alle Mängel auf... Am Ende hilft nur systematisches Testen: Testwert-Tabellen Testwert-Generatoren Sammeln von Testfällen 6
Testtechniken Manueller Test: Eingabe / Aufruf machen "gucken", ob das erwartete Verhlaten vorliegt. Vorteil: Inutition deckt Unerwartetes auf Problem: Abdeckung, Wiederholbarkeit, Ermüdung Verhaltensbeobachtung: Testausgaben in den Code streuen Ablauf im Debugger nachverfolgen und bewerten Vorteil: Testausgaben und Breakpoints wiederverwendbar Problem: Aufwand und Aufmerksamkeit 7 7
Automatisierter Test Die Testfälle werden programmiert Die Entscheidung über Gelingen und Scheitern wird ebenfalls automatisiert Nachteil: Programmieraufwand Vorteil: Beliebig viel beliebig oft wiederholbar Programmieraufwand Wiederholbarkeit 8
Testen während der Entwicklung! Analyse Spezifi-kation Entwurf Im veralteten Wasserfallmodell der Software- Entwicklung kommt dersystemtest ganz am Ende. Top oder Flop, wenn alles schon getan ist! viele Fehler "im Inneren" bleiben unentdeckt Heute wird während der Entwicklung immer das ganze System getestet Systemtest Implementierung Inbetriebnahme 9 9
Nicht nur das Gesamtsystem testen! Analyse Spezifi-kation Entwurf Beispiel Filmregister: Außenverhalten im Test richtig Eintragen und Finden klappt in den Tests trotz fehlerhafter innerer Struktur: "A beautiful Mind" ungleich "A Beautiful Mind" zwei Einträge, die nicht gemeinsam aktualisiert werden irgendwann später fliegen die Fehler auf! Systemtest Implementierung Inbetriebnahme 10 10
Modultests mit main-methoden Jede Klasse besitzt eine main-methode zum Modultest Testen aller exportierten Methoden Testen der inneren Datenstuktur Baut auf andere Klassen auf, die als getestet angenommen werden SupportLine InputReader Responder ResponseMap MapElement 11 11
Testautomatisierung mit JUnit Ein JUnit-TestCase ist eine Klasse, die eine andere Klasse testet TestCase enthält Tests Methoden, deren Ergebnis spezifiziert ist und von JUnint überprüft wird: TestCase enthält Fixtures, mit denen Testbedingungen geschaffen (und aufgelöst) werden: 12 12
Benutzung in Eclipse TestCase erzeugen: New > JUnit TestCase Name = <Klasse>Test Paket = tests TestCase ausführen mit Run As JUnit Test 13 13
SupportLine: MapElement Forderung: Ein MapElement darf nicht zwei Null-Parameter haben! gescheitert (rot) TestCase Test scheitert es wird keine Exception geworfen 14
Gelungener Test "Repariert" (als Beispiel, über den Sinn kann man streiten) gelungen (grün) Test wiederholen (Rerun) 15 15
Weiter mit der ResponseMap Contains sollte beliebige Strings korrekt verarbeiten, aber keinen leeren und keinen null-key akzeptieren! 16 16
Was klappt nicht bei "contains"? Datenstruktur ist ArrayList<MapElement> responselist Aktuelle Lösung Korrektur: repariert 17 17
Zweites Problem: leerer String als Key Leeren String abfangen: Hinweis: Die Verwendung von unchecked Exceptions ist zunächst einfacher, muss aber später noch einmal überdacht werden! 18 18
Wird ein (null,null)-eintrag verhindert? put(null, null) sollte Exception werfen: aber: Grund: irgendwann mal "ausgeblendet" 19 19
Regressionstest Problem im letzten Beispiel: Vor dem Testlauf von ResponseMap hätte ein Testlauf für MapElement durchgeführt werden müssen! Was gestern noch funktionierte, kann heute schon Probleme mit sich bringen (Ariane 5!) Abhilfe: Nach jeder Änderung ALLES durchtesten. Ein Hoch auf die Testautomatisierung... Regressionstest = Nach jeder Veränderung wieder auf die alten Tests zurückkommen. 20
ALLES durchtesten Test-Suiten Alle Tests von Hand nacheinander laufen zu lassen, ist mühsam fehlerträchtig Automatisierung als Test-Suite-Klasse üblicherweise AllTests genannt: 21 21
JUnit im Überblick Test Case (nicht annotiert): Klasse mit einer Menge von Tests (ohne Reihenfolge!) und Fixtures typischerweise einer Klasse zugeordnet Fixture ("Inventar") Operationen vor und nach einem Test bzw. allen Tests eines Testcase @Before, @BeforeClass, @After, @AfterClass Test @Test Methode, deren Ergbenis von JUnit überprüft wird mit expects, assert, fail Gelngen / Scheitern spezifiziert TestSuite @RunWith(Suite.class) @Suite.classes({...}) Klasse heißt typischerweise AllTests und ist leer. TestCases werden in der gegebenen Reihenfolge durchgeführt 22 22
JUnit-Test-Annotationen Quelle: http://www.vogella.com/tutorials/junit/article.html 23 23
JUnit Assert-Statements Quelle: http://www.vogella.com/tutorials/junit/article.html 24 24
Ist das Praxis? Unit Tests sind heute ein MUSS in der Software-Entwicklung Ursprung in der Agilen Software-Entwicklung (xp) Allgemein üblich in der Open Source-Entwicklung: große, weltweite Entwicklergemeinde jeder muss gründlich testen, ehe er eine Änderung veröffentlicht auf dem Server laufen automatische Akzeptanztests, die für jede veröffentlichte Änderung gefahren werden JUnit ist selbst ein Open Source-Projekt Automatisierung geht noch weiter: Werkzeuge zum Compilieren und Bereitstellen führen automatische Tests aus (Hudson, Jenkins...) "Nightly Build" Jede Nacht einen neue, durchgetestete Version der Software. 25 25
Nur für Java? Unit Tests (Modultests) sind zunächst ein Konzept der objektorientierten Programmierung SUnit für Smalltalk war der Prototyp In ähnlicher Form für alle OO-Sprachen vorhanden NUnit für das.net-framework, PHPUnit für PHP, CppUnit für C++, DUnit für Delphi, QUnit für JavaScript, etc. Als Konzept auch für andere Sprachen und grafische Oberflächen weitergeführt Große Testframeworks für heterogene Projekte Selenium für Webanwendungen 26 26
Test Driven Development (TDD) Ein API oder Klasseninterface wirft viele Fragen auf : Beispiel MovieRegister-Interface Was soll beim Eintragen eines leeren Titels passieren? Was soll passieren, wenn der englische und der deutsche Titel gleich sind? Was soll passieren, wenn addmovie() für einen bereits vorhandenen Film aufgerufen wird? Was soll passieren, wenn bei updatemovie eine leere Schauspielerliste angegeben wird, die vorhandene aber nicht leer ist? Darf man mit contains auch nach null und "" suchen? Darf ein Titel beliebigesonderzeichen enthalten? Wie genau müssen Titel übereinstimmen, damit sie identifiziert werden (Groß/Kleinschreibung, Leerzeichen, Tippfehler)? Wie soll das System "fast gleiche" Titel behandeln?... 27 27
Spezifikation durch Testfälle Man kann die Fragen durch Kommentare beantworten Im TDD wird aus jeder Frage einen Testfall mit definiertem Ausgang machen Das ist genauer Das ist fürs Testen sofort nützlich Fragen und Unklarheiten werden auf den Punkt gebracht TDD bedeutet: erst die Testfälle festlegen dann implementieren dann testen 28 28
Falsche Forderung im nachträglichen Test Forderung: im Supportline ontext (Gesamtsystem): Ein Map-Element MapElement darf darf keinen nicht null-schlüssel zwei Null-Parameter haben, wenn haben! derwert nicht null ist. TestCase gescheitert (rot) Test scheitert es wird keine Exception geworfen 29
TDD ist besser Das Beispiel zeigt: nachträglich formuliert man leicht falsche Bedingungen Spezifikaition durch Testfälle bedeutet Präzisierung frühzeitiges Nachdenken über Grenzfälle à TDD ist führt zu besserem Code à den Tester-Blick braucht man trotzdem TDD ist Standard in der agilen Entwicklung! 30 30
... und trotz aller Automatisierung: Der Tester mit dem "Riecher" wird gebraucht Ab jetzt wird jeder Code getestet! J 31 31