$KK(Akzeptanz- und UnitTests) $KKK(Unterschiede zwischen Akzeptanz und Unittests) Akzeptanztests testen das gesamte System aus Sicht des Kunden von aussen(BlackBox Test). I.a. simulieren sie ein Testskript das ein Testteam vor Inbetriebnahme einer neuen Version durchspielt. Die Akzeptanztests werden in enger Zusammenarbeit mit den Kunden geschrieben. Unittests testet die Einzelteile (i.a auf Klassenebene)des Systems aus Sicht des Programmierers (WhiteBox Test). Es wird i.a. jeweils nur eine Klasse getestet, ggf. noch die Interaktion mit angrenzenden Klassen. Es sollte keine Funktionalität in einer Klasse geben, die nicht durch Unittests abgedeckt wird. Was wird getestet ? * Akzeptanztest testen das gesamte System, und können von Anwender nachvollzogen werden. Akzeptanztests werden in enger Zusammenarbeit mit dem Kunden definiert. * Unittests testen eine Programmiereinheit i.a. eine Klasse. (Dies ist nicht zwingend). Unittests werden von Programmierer entwickeln. Wie häufig wird getestet ? * Akzeptanztests werden täglich oder wöchentlich ausgeführt. Nach jeder Integration und vor einer Auslieferung * Unittests werden eigentlich andauernd, nach jeder Änderung ausgeführt. Also in 2 bis 15 Minuten Takt. Es werden dabei immer alle Unittests ausgeführt und nicht nur dijenigen an den entsprechenden Klassen gearbeitet worden ist. Was sind die Erfolgskriterien für Tests ? * Akzeptanztests dürfen ruhig fehlschlagen. Der Erfolg sollte allerdings monoten wachsend sein. Erst am Projektende sollten alle Akzeptanztests korrekt ablaufen. * Unittests sollten jederzeit immer zu 100 % korrekt ablaufen. Sollte einer schief gehen, wird nicht mehr weiterentwickelt sondern der Fehler behoben. Testdauer ? * Da Akzeptanztests nicht allzuhäufig ausgeführt werden, können diese Tests ruhig einige Stunden andauern. * Unittests müssen sehr schnell ablaufen, da diese im Minutentakt ausgeführt werden sollen. Wie testet man Zugriff auf externe Systeme ? * Akzeptanztests greifen i.a. auf externe Systeme zu. Allerdings kann nur getestet werden, wenn das Verhalten der externen Systeme konstant ist. Da z.B. bei Unterschiedlichen Datenbankinhalt die Ausgabe verschieden ist, muss vor der dem Test. ein definierter konstanter Datenbankbestand eingeladen werden. Ansonsten sind die Akzeptanztests nicht wiederholbar. * Unittests greifen nie auf externe Systeme zu, diese werden mit Mock Objekten simuliert. Mit welcher Sprache testet man? * Akzeptanztests werden oft mit einer Skriptsprache geschrieben. Am besten ist es wenn die Anwender sie nachvollziehen können. Typisch sind Testsysteme die die Aktionen eines Benutzers an der Oberfläche aufzeichnen. * Unittests schreibt man i.a. immer in der Sprache in der man entwickelt. $KKK(Vorgehensweise Akzeptanztests) Fuer Akzeptanztests braucht man eine spezielle Testdatenbank mit konstant definierten Inhalt. Dieser Inhalt wird einmal gesichert, damit man vor Anstossen der Akzeptanztests diese wieder einladen kann. Ein Akzeptanztest simuliert i.a. eine oder mehrere Benutzeraktionen des zu testeten Programms. Diese Benutzeraktionen werden durch Events programmatisch ausgeloest. Aufgrund der Benutzeraktionen erfolgen mehr oder weniger komplexe Berechnungen. Um die Korrektheit der Ergebnisse zu ueberpruefen werden Zustand von Objekten im Image bzw. Eintraege in den Datenbanken ueberprueft. Ggf. muss am Ende eines Tests die Testdatenbank wieder bereinigt werden. Ein Testframework protokolliert fuer jeden einzelnen Test den Erfolg. Ggf. verbindet man einzelne Tests mit Performance Messungen. Zur Not kann auch JUnit fuer Akzeptanztests verwendet werden. Im Gegensatz zu Unittests sind bei Akzeptanztests fehlgeschlagene Tests kein Beinbruch. Erst am Projektende sollten auch alle Akzeptanztests laufen. Der Nachteil von [e|JUnit>http://wwww.junit.org] für Akzeptanztests ist daß der Kunde damit nicht umgehen kann. Am besten ist es wenn der Kunde Akzeptanztests selbst aufrufen und die Ergebnisse überprüfen kann. Das Open-Source Tool [e|Fitnesse > http://www.fitnesse.org] ist ein erweiterter Wiki-Server. Er dient gleichzeitig zur Dokumentation und zum Ausführen von Java-Tests. Mit [e|Fitnesse >http://www.fitnesse.org] kann die Dokumentation einer Anforderung und die dazugehörigen ausführbaren Akzeptanztests nebeneinander innerhalb einer einzigen HTML Seite definiert sein. $KKK(Vorgehensweise Unittests) Folgende Vorgehensweisen werden empfohlen: # Lasse zuerst alle Tests laufen, um sicher zu gehen, dass man nicht auf fehlerhaften Code aufbaut. Korrigiere ggf. erst den Fehler. # Schreibe immer zuerst ein Test, dann den dazugehörigen Code # Implementiere immer genau soviel, dass die Tests korrekt ablaufen. # Entwickle in möglichst kleinen Schritten # Geht ein Test schief, wird nicht weiterentwickelt sondern erst der Fehler korrigiert # Refaktoriere nach jedem Test, falls möglich. # Oft macht es Sinn, ersteinmal absichtlich im Code einen Fehler einzubauen, um zu testen ob der TestCase korrekt implementiert ist. Beispiel: Zu Entwickeln ist eine Klasse Mitarbeiter # Lasse alle vorherigen Tests laufen, diese muessen zu 100% laufen. # Zunächst soll das Erzeugen eines Mitarbeiters getestet werden. # Versioniere den Code, um ggf. falls alles schiefgeht schnell zu einem korrekten Ausgangszustand zurückkehren kann. ## Überlege wie die einfachst möglichste Schnittstelle aussieht um einen Mitarbeiter zu erzeugen ## Schreibe einen TestCase, mit dieser Schnittstelle, der überprüft ob das Erzeugen eines Mitarbeiters funkioniert. ## Lasse die TestCases laufen. Es wird ein Compiler Fehler geben, da die Schnittstelle noch nicht implementiert ist. ## Implementiere die Schnittstelle gerade soweit, dass der Test ohne Compilerfehler abläuft. ## Lasse die TestCases laufen.Der aktuelle TestCase wird zwar ablaufen aber einen Fehler anzeigen,da die Funktionaliät ## ja immer noch nicht implementiert ist. Wird kein Fehler gemeldet, so ist der Test falsch. ## Implementiere die Funktionalitaet, ohne auf Refaktorierungen zu achten. ## Lasse alle TestCases laufen. Korrigiere den Code solange bis alle TestCases korrekt ablaufen. ## Kann man die Tests oder den SourceCode refaktorieren?. Falls ja, muss dies gemacht werden. Ggf. vorher den Code wieder versionieren. ## Lasse nach der Refaktorisierung wieder alle TestCases durchlaufen, um die Korrektheit der Aenderungen zu garantieren. # Nun soll garantiert werden, dass es nicht moeglich ist zwei Mitarbeiter zu erzeugen, die dieselbe Personalnummer haben. ## Schreibe zunaechst einen TestCase der die Funktionalitaet überprueft ## Nun muss wieder beim Ablauf aller TestCases ein Fehler auftreten. ## Implementiere die Funkionaliaet gerade soweit dass die TestCases laufen. ## Kontrolliere durch den Ablauf aller TestCases, dass die Funktionalität korrekt ist. ## Refaktoriere ## Es werden wieder alle TestCases durchlaufen, um zu testen daß durch das Refaktorieren keine Fehler entstanden sind #Für alle anderen Funktionaliäten gehen wir genauso vor: ## Erst einen Test schreiben ## Tests laufen lassen, es muss einen Fehler ergeben. ## Implementierte gerade soweit, daß der Test korrekt abläuft ## Tests laufen lassen, falls Test nicht korrekt muss ein Fehler im Test oder im Code sein. Fehler muessen sofort behoben werden. ## Refaktoriere ## Alle Tests laufen lassen, um die Korrektheit des Refaktorierens zu garantieren. Achtung: # Wenn alle Tests korrekt durchlaufen, heisst das noch lange nicht daß der Code fehlerfrei ist. Ein korrekter Durchlauf sagt nichts über die Qualität der Tests aus. # Das Entwickeln von Unittests ist alles andere als trivial und muss gelernt werden. Oft kommt die Projektleitung erst auf die Idee Unittests einzuführen, wenn die Qualität des Codes schon sehr schlecht ist. Unittests im Nachhinein einzuführen ist aber auch für erfahrenen Unittester eine schwere Herausforderung. Wenn die Qualität des existierenden Codes sehr schlecht ist, hilft nur noch neu schreiben. $KKK(Schwierigkeiten Unittests) Das Ziel ist es mit Unittests eine 100% Testabdeckung zu erreichen. Dies ist allerdings i.a. nicht moeglich. *Schwierigkeiten Benutzungsoberflaeche zu testen Die einfachste Lösung ist Benutzungsoberflaechen nicht zu testen. Dies geht nur wenn die Benutzungoberflächenobjekte sehr duenn sind. D.h es werden nur die Widgets dargestellt, jegliche Logik wird ausgelagert. Man trennt die Benutzungsoberflaeche in eine Interaktionsschicht und eine Funktionsschicht. ( siehe [e|OO-Design Kurs>www.cocodil.org/OO-Desingkurs/einfuehrung.htm]). Die Objekte der Funktionsschicht wird mit Unittests überprueft. *Zugriff auf externe Systeme sind teuer Zugriff auf Datenbanken und Hostsysteme dauern lange, und man kann nichts testen falls diese ausfallen. Die Idee ist diese Systeme zu simulieren(Mockobjekte). Es gibt im Internet existierende Frameworks für Mockobjekte. Falls man Mockobjekte falsch programmiert, beteht die Gefahr dass sie die Tests verfälschen. Der richtige Umgang mit Mockobjekten kann aber Tests erheblich vereinfachen. *Threads Threads werden verwendet um Prozesse im Hintergrund ablaufen zu lassen. So kann ein Anwender Eingaben in seinen Fenstern betätigen, während der Rechner komplexe Algorithmen berechnet. Mit Threads haben wir leider oft ein nichtdeterministisches Verhalten. Je nachdem welcher Thread schneller oder langsamer abläuft, kann das Gesamtverhalten korrekt oder falsch sein. Eine mögliche aber unschöne Lösung dieselben Unittests öfters als einmal ablaufen zu lassen. Die Wahrscheinlichkeit einen Defekt zu finden wird höher, aber den Beweis auf Korrektheit haben wir damit nicht. Zunächst muss überprüft werden, ob die Threads überhaupt benötigt werden. In vielen Fällen bekommen wir durch Einführung von Threads einen Performance Verlust. Oft kann man statt einem Thread (d.h. leichtgewichtiger Prozess der sich mit anderen Prozessen gemeinsamen Adressraum teilt) einen richtigen Prozess verwenden (ein eigenstaendiges Programm, das seinen eigenen Adressraum hat). Wenn man nicht schon auf Threads verzichten kann, sollte man sich an die gängigen Patterns (siehe [e|Buch von Frank Buschmann > http://www.amazon.de/exec/obidos/ASIN/3898641422/qid=1048778843/sr=2-3/ref=sr_aps_prod_3_2/028-7705194-2175707]) halten. * keine Moeglichkeit auf private Methoden zuzugreifen Das Problem gibt es in Smalltalk zum Glück nicht, da sind alle Methoden öffentlich. In meinen ersten Erfahrung in Java löse ich das Problem dadurch, dass ich kaum private Methoden verwende (auch keine optimale Loesung). Da ich die Tests im selben Package habe wie die zu testende Klassen kann ich auch protected bzw default Methoden testen. Laut Kent Beck und Frank Westphal sollten private Methoden nicht getestet werden, da sie indirekt von öffentlichen Methoden aufgerufen werden. Sie meinen falls wirklich der Bedarf da ist private Methoden zu testen, so deudet es auf ein Design Problem hin. In der XP Gemeinde ist dies aber allgemein umstritten, und auch meine ersten Erfahrungen widersprechen dieser These. (Ich habe bei weitem nicht die Erfahrung von Frank Westphal oder Kent Beck). Es gibt ein Tool JUnitX mit der auch private Methoden getestet werden können.