Ein Modultest (auch Komponententest oder oft vom engl. unit test als Unittest bezeichnet) wird in der Softwareentwicklung angewendet, um die funktionalen Einzelteile (Module) von Computerprogrammen zu testen, d. h., sie auf korrekte Funktionalität zu prüfen.
Der Ausdruck Modultest wird auch als eine frühe Teststufe verstanden,[1] in der die inneren, detailliertesten Komponenten der Software getestet werden. Siehe dazu auch die Grafik Stufen des V-Modells und V-Modell (nach Barry Boehm).
Einordnung im Testprozess
Da Algorithmen auf Modulebene meist nur eine begrenzte Komplexität aufweisen und über klar definierte Schnittstellen aktiviert werden, können sie mit relativ wenigen Testfällen weitgehend vollständig getestet werden. Dies gilt als Voraussetzung für die anschließende Teststufe Integrationstest, um dort die Testfälle auf das integrierte Zusammenwirken größerer Funktionsteile oder der gesamten Anwendung ausrichten zu können; die modulspezifischen Detailkonstellationen lassen sich damit dort auf Stichproben beschränken, was die Anzahl der erforderlichen Testfälle drastisch reduziert.
Zum Vergleich: Ein Gerät wird erst dann als Ganzes getestet, wenn die Funktionsfähigkeit seiner Einzelteile als gesichert gilt.
Testtechniken
Aus dem Spektrum der zahlreichen Testarten/Testtechniken treffen für Modultests besonders die folgenden zu. Ein Modultest ist also zum Beispiel (mehrere Möglichkeiten):
- ein Entwicklertest
- zum Testen werden häufig Testversionen der Software bereitgestellt, um z. B. Eingaben zu simulieren oder Ergebnisse zu protokollieren.
- ein White-Box-Test
- die zu testenden funktionalen Details und ihre möglichen Kombinationen werden i. d. R. aus dem Quellcode oder sehr detaillierten technischen Spezifikationen abgeleitet.
- ein Überdeckungstest
- unterschiedliche Test-Abdeckungsgrade der codierten (Kontroll-) Anweisungen werden beim Testen angestrebt.
- ein Äquivalenzklassentest
- die Testkonstellationen können damit auf das Testen bestimmter Grenzwerte beschränkt werden.
Verfahren zur Testfalldefinition
Modultests zählen zu den White-Box-Tests. Das heißt, dass bei der Definition der Testfälle der zu testende Quellcode bekannt ist. Die Spezifikation der Software wird lediglich für die Bestimmung der Soll-Ergebnisse benutzt. Prinzipiell müssen alle Quellcode-Teile mindestens einmal ausgeführt werden. Anweisungsüberdeckung, Zweigüberdeckung oder Pfadüberdeckung können dabei helfen festzustellen, welche Testfälle hierzu in der Theorie mindestens erforderlich sind (siehe dazu Kontrollflussorientierte Testverfahren). In der Praxis versucht man in aller Regel, das gesetzte Überdeckungsziel mit möglichst wenigen Testfällen zu erreichen, da alle Modultests auch laufend gepflegt werden müssen.
Verfahren zum Erstellen von Modultests
Üblicherweise orientieren sich alle Modultests an einem einheitlichen Grundaufbau. Dabei wird zunächst (1) ein Ausgangszustand initialisiert, hierauf (2) die zu testende Operation ausgeführt und zuletzt (3) das Ist-Ergebnis mit einem aus der Spezifikation abgeleiteten Sollwert verglichen. Für diese Vergleiche stellen die Test Frameworks assert
-Methoden (deutsch etwa: feststellen, versichern) zur Verfügung.
Eigenschaften von Modultests
Isolierung von Testobjekten
Modultests testen ein Modul isoliert, d. h. weitgehend ohne Interaktion mit anderen Modulen. Deshalb müssen oder können bei Modultests andere Module beziehungsweise externe Komponenten wie etwa eine Datenbank, Dateien, Backendsysteme oder Unterprogramme durch Hilfsobjekte simuliert werden, soweit das zu testende Modul (Prüfling oder Testobjekt) diese benötigt. Vollständige Tests mit allen Komponenten in ihrer Originalversion sollten in den nachfolgenden Integrations- und Systemtests durchgeführt werden – wobei ggf. im Modultest nicht erkannte Fehler (z. B. wegen identischer Falschannahmen für das Testobjekt und die Hilfsroutine) entdeckt werden sollten.
Derartige Hilfsobjekte werden z. B. als Stellvertreter implementiert und mittels Inversion of Control bereitgestellt. Ein Modul kann so meist einfacher getestet werden, als wenn alle Module bereits integriert sind, da in diesem Fall die Abhängigkeit der Einzelmodule mit in Betracht gezogen und im Testhandling berücksichtigt werden müsste. Auch sind derart isolierte Modultests schon möglich, wenn andere, eigentlich benötigte Komponenten für den Test noch nicht verfügbar sind. Bekannt sind folgende Hilfsobjekte:[2]
- Dummy
- Ein Objekt, das im Code weitergereicht, aber nicht verwendet wird. Wird eingesetzt, um Parameter mit Werten zu befüllen.
- Fake
- Ein Objekt mit Implementierung. Die Implementierung ist dabei jedoch eingeschränkt, wodurch ein Einsatz in der Produktionsumgebung nicht möglich ist. Ein typisches Beispiel für ein Fake ist eine Datenbank, die Daten nur temporär im Speicher hält.
- Stub
- Ein Objekt, welches beim Aufruf einer bestimmten Methode unabhängig von der Eingabe die gleiche Ausgabe liefert.
- Mock
- Ein Objekt, das bei vorher bestimmten Funktionsaufrufen mit bestimmten übergebenen Werten eine definierte Rückgabe liefert. Zur Erstellung des Mock-Objektes verwendet man üblicherweise ein Mocking Framework.
- Spy
- Ein Objekt, welches Aufrufe und übergebene Werte protokolliert und bei Bedarf zurückliefert. Dabei werden Fake-, Stub- oder Mock-Objekte zu einem Spy erweitert. Alternativ kann ein Decorator eingesetzt werden.
- Shim, Shiv
- Eine Bibliothek, welche die Anfrage an eine Programmierschnittstelle abfängt und selbst behandelt (z. B. mittels eines Fake-, Stub- oder Mock-Objekts), die übergebenen Parameter verändert oder die Anfrage umleitet.
Test des Vertrages und nicht der Algorithmen
Modultests testen gemäß dem Design-by-contract-Prinzip möglichst nicht die Interna einer Methode, sondern nur ihre externen Auswirkungen (Rückgabewerte, Ausgaben, Zustandsänderungen, Zusicherungen). Werden die internen Details der Methode geprüft (dies wird als White-Box-Testing bezeichnet), könnte der Test fehlschlagen, obwohl sich die externen Auswirkungen nicht geändert haben. Daher wird in der Regel das sogenannte Black-Box-Testing empfohlen, bei dem man sich auf das Prüfen der externen Auswirkungen beschränkt.
Automatisierte Modultests
Mit der Verbreitung von agilen Softwareentwicklungsmethoden und insbesondere testgetriebener Entwicklung ist es üblich geworden, Modultests möglichst automatisiert auszuführen. Dazu werden üblicherweise mit Hilfe von Test Frameworks wie beispielsweise JUnit Testprogramme geschrieben. Über die Test Frameworks werden die einzelnen Testklassen aufgerufen und deren Komponententests ausgeführt. Die meisten Test Frameworks geben eine grafische Zusammenfassung der Testergebnisse aus.
Automatisierte Modultests haben den Vorteil, dass sie einfach und kostengünstig ausgeführt und dass neue Programmfehler schnell gefunden werden können. Dies setzt allerdings u. a. voraus, dass Testergebnisse aus früheren Modultests vorliegen und die aktuellen Testergebnisse mit diesen verglichen werden können.
Kritik
Modultests können (wie jeder Test) die Fehlerfreiheit des getesteten Moduls nicht garantieren oder nachweisen, sondern lediglich unterstützen. Die Grenzen von Modultests liegen notwendigerweise darin, dass nur solche Fehler gefunden werden können, zu deren Entdeckung die verwendeten Tests geeignet sind. Eine Softwarekomponente, die „grün“ testet, ist also nicht unbedingt fehlerfrei.
Das Merkmal von Code, „grün“ zu testen, und durchaus auch der Wunsch nach diesem Ergebnis, könnte dazu führen, dass tatsächlich (unbewusst) nur soviel getestet wird, dass alle Tests „grün“ sind. Module, die keine fehlschlagenden Modultests haben, als fehlerfrei zu behandeln, ist ein häufiger Fehlschluss in der Praxis testgetriebener Entwicklung.
Bei der Änderung von Code stellen Modultests sicher, dass keine solchen Fehler unbemerkt eingefügt werden, die der vorhandene Bestand an Tests sicher aufdeckt. Das Problem, eine ausreichende Testabdeckung herzustellen, kann nur durch entsprechende Beachtung und geeignete Maßnahmen gelöst werden.
Wenn – wie üblicherweise – der Autor von Modultests mit dem Autor der Module identisch ist, können Denkfehler in der Implementierung auch im Test erscheinen und nicht aufgedeckt werden. Wenn es sich um dieselbe Person handelt, wird dies auch nicht dadurch ausgeschlossen, dass die Tests zuerst entwickelt werden, da sowohl die beabsichtigte Funktionsweise des Codes, als auch seine zukünftige Gestalt bereits im Denken des Testautors und späteren Codeautors präsent sein können.
Siehe auch
Literatur
- Paul Hamill: Unit Test Frameworks. (A Language Independent Overview). O’Reilly Media, Beijing u. a. 2004, ISBN 0-596-00689-6 (englisch).
- Gerard Meszaros: xUnit Test Patterns. Refactoring Test Code. Addison-Wesley, Upper Saddle River NJ u. a. 2007, ISBN 978-0-13-149505-0 (englisch).
- Andreas Spillner, Tilo Linz: Basiswissen Softwaretest. Aus- und Weiterbildung zum Certified Tester. Foundation Level nach ISTQB-Standard. 4., überarbeitete und aktualisierte Auflage. dpunkt-Verlag, Heidelberg 2010, ISBN 978-3-89864-642-0.
Weblinks
- Linkkatalog zum Thema Unit-Testing bei curlie.org (ehemals DMOZ) (englisch)
- Einführung in die Prinzipien von Unit-Tests
- Artikel zum Thema Komponententests unter Java (englisch)
Einzelnachweise
- ↑ Martin Pol, Tim Koomen, Andreas Spillner: Management und Optimierung des Testprozesses. Ein praktischer Leitfaden für erfolgreiches Testen von Software mit TPI und TMap. 2. Auflage. dpunkt, Heidelberg 2002, ISBN 3-89864-156-2.
- ↑ Martin Fowler: Mocks Aren’t Stubs. 2. Januar 2007, abgerufen am 5. August 2013 (englisch).