Inhalt Abbildung PDF Source Projektmanagement
 |<    <     >    >|  Generated by CoCoDiL

2.3 Akzeptanz- und UnitTests

2.3.1 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 ?

Wie häufig wird getestet ?

Was sind die Erfolgskriterien für Tests ?

Testdauer ?

Wie testet man Zugriff auf externe Systeme ?

Mit welcher Sprache testet man?

2.3.2 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 * JUnit 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 * Fitnesse ist ein erweiterter Wiki-Server. Er dient gleichzeitig zur Dokumentation und zum Ausführen von Java-Tests. Mit * Fitnesse kann die Dokumentation einer Anforderung und die dazugehörigen ausführbaren Akzeptanztests nebeneinander innerhalb einer einzigen HTML Seite definiert sein.

2.3.3 Vorgehensweise Unittests

Folgende Vorgehensweisen werden empfohlen:

  1. Lasse zuerst alle Tests laufen, um sicher zu gehen, dass man nicht auf fehlerhaften Code aufbaut. Korrigiere ggf. erst den Fehler.
  2. Schreibe immer zuerst ein Test, dann den dazugehörigen Code
  3. Implementiere immer genau soviel, dass die Tests korrekt ablaufen.
  4. Entwickle in möglichst kleinen Schritten
  5. Geht ein Test schief, wird nicht weiterentwickelt sondern erst der Fehler korrigiert
  6. Refaktoriere nach jedem Test, falls möglich.
  7. 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

  1. Lasse alle vorherigen Tests laufen, diese muessen zu 100% laufen.
  2. Zunächst soll das Erzeugen eines Mitarbeiters getestet werden.
  3. Versioniere den Code, um ggf. falls alles schiefgeht schnell zu einem korrekten Ausgangszustand zurückkehren kann.
    1. Überlege wie die einfachst möglichste Schnittstelle aussieht um einen Mitarbeiter zu erzeugen
    2. Schreibe einen TestCase, mit dieser Schnittstelle, der überprüft ob das Erzeugen eines Mitarbeiters funkioniert.
    3. Lasse die TestCases laufen. Es wird ein Compiler Fehler geben, da die Schnittstelle noch nicht implementiert ist.
    4. Implementiere die Schnittstelle gerade soweit, dass der Test ohne Compilerfehler abläuft.
    5. Lasse die TestCases laufen.Der aktuelle TestCase wird zwar ablaufen aber einen Fehler anzeigen,da die Funktionaliät
    6. ja immer noch nicht implementiert ist. Wird kein Fehler gemeldet, so ist der Test falsch.
    7. Implementiere die Funktionalitaet, ohne auf Refaktorierungen zu achten.
    8. Lasse alle TestCases laufen. Korrigiere den Code solange bis alle TestCases korrekt ablaufen.
    9. Kann man die Tests oder den SourceCode refaktorieren?. Falls ja, muss dies gemacht werden. Ggf. vorher den Code wieder versionieren.
    10. Lasse nach der Refaktorisierung wieder alle TestCases durchlaufen, um die Korrektheit der Aenderungen zu garantieren.
  4. Nun soll garantiert werden, dass es nicht moeglich ist zwei Mitarbeiter zu erzeugen, die dieselbe Personalnummer haben.
    1. Schreibe zunaechst einen TestCase der die Funktionalitaet überprueft
    2. Nun muss wieder beim Ablauf aller TestCases ein Fehler auftreten.
    3. Implementiere die Funkionaliaet gerade soweit dass die TestCases laufen.
    4. Kontrolliere durch den Ablauf aller TestCases, dass die Funktionalität korrekt ist.
    5. Refaktoriere
    6. Es werden wieder alle TestCases durchlaufen, um zu testen daß durch das Refaktorieren keine Fehler entstanden sind
  5. Für alle anderen Funktionaliäten gehen wir genauso vor:
    1. Erst einen Test schreiben
    2. Tests laufen lassen, es muss einen Fehler ergeben.
    3. Implementierte gerade soweit, daß der Test korrekt abläuft
    4. Tests laufen lassen, falls Test nicht korrekt muss ein Fehler im Test oder im Code sein. Fehler muessen sofort behoben werden.
    5. Refaktoriere
    6. Alle Tests laufen lassen, um die Korrektheit des Refaktorierens zu garantieren.

Achtung:

  1. 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.
  2. 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.

2.3.4 Schwierigkeiten Unittests

Das Ziel ist es mit Unittests eine 100% Testabdeckung zu erreichen. Dies ist allerdings i.a. nicht moeglich.

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 * OO-Design Kurs). Die Objekte der Funktionsschicht wird mit Unittests überprueft.

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 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 * Buch von Frank Buschmann ) halten.

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.

Inhalt Abbildung PDF Source Projektmanagement
 |<    <     >    >|  Generated by CoCoDiL