Teststufen oder Testebenen
Teststufen sind ein sehr praktikables Modell, um Testaktivitäten zu strukturieren. Jede dieserTeststufe deckt dabei einen anderen Teil der Software...
Der Unittest ist für mich die essentiellste aller Teststufen. Auf sie schaue ich auch zuerst, wenn ich ein neues Beratungsprojekt starte. Warum? Hier entsteht die Robustheit der Applikation. Ohne ausreichende Unittests sind stabile Integrations-, System- und Abnahmetests schwer möglich. Gute Unittests helfen massiv dabei, dass der Code konsistent bleibt. Probleme, dass das System durch Änderungen instabil wird oder sich unerwartete Seiteneffekte zeigen, lassen sich mit Unittests im Vorfeld minimieren. Und sie sind ein ausgezeichneter Indikator zur Qualität der Architektur und des Designs. Ein “Unittest ist hier nicht möglich” deutet stark darauf hin, dass hier potentielles Ungemach schlummert. Spätestens beim Refactoring dieses Bereichs fehlt dann die Qualitätssicherung in Form von Tests. Unittests sind ein idealer Feedbackmechanismus bei der Software-Entwicklung.
Ändert oder erweitert man den Code, liefern Unittests sofort Feedback, ob man damit etwas kaputtgespielt hat. Das ist wiederum der Grund, warum Unittests auch rasant ausführbar sein müssen. Geht das nicht, ist das wieder ein Indiz dafür, nochmal einen Blick auf die Architektur und das Design zu werfen.
In der Literatur wird die Teststufe als Komponententest definiert und taucht unter diesem Begriff häufig auf. Auch Modultest ist ein gängiges Synonym. Das ist natürlich auch etwas abhängig von der Programmiersprache und dem Kontext. In der Projektpraxis hat sich jedoch Unittest durchgesetzt. Ich kann mich an kein Projekt der letzten 20 Jahren erinnern, wo diese Teststufe nicht Unittest hieß.
Das ISTQB definiert Komponententest als: “Eine Teststufe mit dem Schwerpunkt auf einer einzelnen Hardware- oder Softwarekomponente.”
Generell kann man sagen: Unittest ist der Test der kleinsten Einheit. Ob diese jetzt Modul, Klasse, Anweisung oder sonst sie heißt.
Als Testbasis für den Unittest dienen alle Informationen, die diesen kleinen Funktionsblock beschreiben. Das können aus dem Design oder der Architektur abgeleitete Infos sein, oder Teile einer User Story oder Anforderung. Manchmal gibt es auch Komponentenspezifikationen oder Modelle, die die Funktionalität der Unit beschreiben.
Zur Testfallerstellung beim Unit- oder Modultest eignen sich ganz besonders die strukturierten Testentwurfsmethoden wie Äquivalenzklassenanalyse, Grenzwertanalyse oder Entscheidungstabellen. Ebenso Kombinatoriken wie Pairwise oder Klassifikationsbaum. Gegenüber anderen Teststufen wie Systemtest oder Abnahmetest, hat man hier den Vorteil, dass Wertebereiche, etc. deutlich konkreter sind, was die Ableitung von Testfällen erleichtert.
Durch agile Softwareentwicklung ist Test Driven Development (TDD) sehr populär geworden. Dabei wird nicht zuerst der Programmcode geschrieben und dann der Testcode, sondern umgekehrt. Es wird zuerst der Test geschrieben, dieser schlägt fehl, dann wird der funktionale Code entwickelt, damit der Testfall “grün” wird. In dieser Struktur wird weiterentwickelt. TDD hat viele Vorteile, gerade was den Fokus auf Tests betrifft, aber auch ein paar Nachteile.
Das Testobjekt für den Unittest ist die kleinste Einheit der jeweiligen Programmiersprache, die sinnvoll getestet werden kann. Es geht darum, diese Einheit für sich selbst zu testen, ohne Interaktion mit anderen dieser Einheiten (Klassen, Module, etc.).
Ziel des Unittests ist es, die funktionalen und auch nicht-funktionalen Aspekte auf unterster Ebene zu prüfen. Das hat mehrere Vorteile:
Sehr oft kommt hier das Thema Codeabdeckung zur Sprache. Was versteht man unter Codeabdeckung? Codeabdeckung zeigt, wieviel des Software-Sourcecodes beim Ausführen der Testfälle benutzt werden. Die Codeabdeckung kann auf verschiedene Bestandteile des Codes abzielen. Darum unterscheidet man z.B. zwischen Anweisungsüberdeckung oder Zweigüberdeckung.
Gerne wird Codeabdeckung auch als Testendekriterium oder Definition of Done genutzt. Dann finden sich Formulierungen wie “Unittests müssen 80% Codeabdeckung aufweisen”. Solche Zahlen festzulegen und zu messen ist durchaus ein interessanter Aspekt. Daraus ergibt sich aber ein gravierendes Problem: Die Qualität der Testfälle kann sinken, da versucht wird die Abdeckung mit möglichst einfachen Testfällen zu erreichen. Alle Sonderfälle oder Negativtests (z.B. mit anderen Testdaten) werden weggelassen, da sie nicht oder kaum zur Erhöhung der Testabdeckung beitragen.
Daher sind diese Messlatten wie “80% Codeabdeckung” immer mit Vorsicht zu genießen. Sie können helfen, Bewusstsein für Unittests zu schaffen, aber ebenso zu mehr Fahrlässigkeit führen.
Für Unittests finden sich in der Regel zwei Testumgebungen. Zum Einen die Entwicklungsumgebung des Entwicklers, wo die Testfälle schnell und unkompliziert ausgeführt werden können. Zum Anderen der Build-Server oder die Pipeline, in der die Software für weitere Zwecke gebaut wird. Für beide Varianten stehen heutzutage Unmengen an Werkzeugen, Scripts und Best Practices zur Verfügung.
Da sich Unittests immer nur auf eine Einheit fokussieren, ist die Frage: Was passiert mit dem Rest, z.B. den aufrufenden oder den aufgerufenen Klassen. Diese werden typischerweise durch Testtreiber und Stubs ersetzt, die die notwendigen Aufrufe und Antworten simulieren.
Durch die Modularität und Kleinteiligkeit der Unittests ist der Umgang mit Testdaten meist gut umsetzbar. Diese müssen nur einen gewissen Aspekt abdecken und können daher leicht aufgebaut bzw. gelöscht werden. Sie werden entweder direkt bei den Testfällen verortet oder in einem gemeinsamen Repository. Auch ein Testdatengenerator macht in größeren Projekten Sinn.
Testautomatisierung ist in Unittests ein No-Brainer. Es gibt für alle gängigen Programmiersprachen mittlerweile Frameworks, die sowohl bei der Erstellung als auch bei der Durchführung der Unittests unterstützen. Und das sowohl in den Entwicklungsumgebungen als auch in den Build-Pipelines.
Unittests sind in allen Projektarten notwendig und mittlerweile auch gut vertreten. In agilen Projekten haben sie auch von Anfang an einen hohen Stellenwert genossen. Durch das laufende Refactoring und die kurzen Zyklen, in denen die Software gebaut wird, sind sie hier als Feedback essentiell.
In Projekten treffe ich immer wieder auf drei Probleme, die es zu lösen gilt:
Ein Mindestmaß an Unittests ist bereits- mehr als die anderen Teststufen- in der Praxis angekommen. Dennoch gibt es häufig Probleme bei der Umsetzung. Zu oft liegt der Fokus rein auf der Codeabdeckung oder die Testfallentwicklung wird als lästiges Übel wahrgenommen. Dabei sind gerade auf dieser Ebene so viele Quick-Wins für den Projekterfolg zu machen.
Teststufen sind ein sehr praktikables Modell, um Testaktivitäten zu strukturieren. Jede dieserTeststufe deckt dabei einen anderen Teil der Software...
Zugegeben, dem Auslagern von Testaktivitäten – egal ob in die Nähe oder in die Ferne – stehe ich skeptisch gegenüber. Zum Testen und dem Drumherum...
Der Systemtest ist eine sehr spannende Teststufe. Unittest und Integrationstest fokussieren sich mehr auf die inneren Aspekte der Applikation. Im...