Donnerstag, 24 Mai 2023

Warum die meisten automatisierten Softwaretests kaum Mehrwert bringen

Christoph Ebeling

Founder & Managing Director

"Automatisierte Softwaretests können keine manuellen Tests ersetzen." Dieser und ähnliche Sätze höre ich oft in Gesprächen mit Kunden. Dies mag für viele Entwickler auf den ersten Blick kontraintuitiv erscheinen, ist jedoch in vielen Fällen zutreffend. Allerdings hat dies nichts damit zu tun, dass automatisierte Tests an sich nicht sinnvoll wären. Vielmehr sind häufig Test- oder Softwarearchitekturen schlichtweg falsch aufgebaut und erzielen daher nicht den gewünschten Return on Investment (ROI). Wir wollen uns ansehen, warum dies so ist und was wir tun können, um solche Tests zukünftig zu vermeiden.

Unit-Tests als Produktivitätskiller

Zunächst einmal ist es wichtig, sich die Frage zu stellen, warum wir Software überhaupt testen. Denn viele Entwickler schreiben Tests lediglich aus der Intention heraus, dass es zum guten Ton gehört, neu implementierten Code zu testen. Die Frage, ob der Code getestet wurde, wird dann meist durch die - bei falscher Verwendung sehr irreführende - Metrik "Line Coverage" bemessen. Dies ist gleich in mehrerer Hinsicht problematisch.

Zum einen verführt diese Metrik zur übermäßigen Verwendung von Unit-Tests. So werden nicht selten Getter- und Setter-Methoden getestet oder Komponenten, deren Aufgabe einfache Delegation ist, mit aufwendigen Tests und massivem Einsatz von Mocking getestet. Wie den meisten erfahrenen Engineers bewusst sein dürfte, sind solche Tests nicht nur wenig aussagekräftig, sondern auch äußerst problematisch zu warten, da jeder Test nicht nur einmalig erstellt, sondern auch kontinuierlich gewartet werden muss. Dies hat zur Folge, dass sich das Entwicklungsteam darauf verlässt, dass ein Stück Quellcode angeblich getestet wurde, obwohl dies eigentlich gar nicht der Fall ist.

Hinzu kommt noch, dass sich Entwickler mit der Wartung eines solchen Tests beschäftigen müssen, was in der Regel viel Zeit und Geld kostet. Zwar haben Unit-Tests definitiv ihre Daseinsberechtigung, jedoch nur bei Komponenten, die ein entsprechendes Maß an Geschäftslogik beinhalten.

Nicht jede Architektur ist testbar

Hiermit kommen wir direkt zum nächsten Problem. Nicht jede Art von Softwarearchitektur ist gleich gut testbar. Wie bereits angesprochen, erfordern Komponenten, in denen hauptsächlich Delegation, API-Calls oder Datenbankzugriffe gemacht werden, andere Tests als beispielsweise eine Funktion für eine Preiskalkulation, welche aufgrund ihrer zahlreichen Edge-Cases nur mit Unit-Tests sinnvoll getestet werden kann. Was aber, wenn diese beiden Teile vermischt wurden? Dann haben wir entweder einen Integrationstest, für den wir aufgrund vieler Edge-Cases eine große Menge an Fixtures in die Datenbank laden müssen oder aber einen Unit-Test, der ein nicht unerhebliches Maß an Mocks braucht. Da die Erstellung von Mocks und Fixtures jedoch einer der größten Zeitfresser im Softwaretesting ist, kann eine solche Testweise kaum effizient sein. Die Voraussetzung für effizientes Testen ist somit die Trennung von Infrastruktur und Geschäftslogik, wie sie beispielsweise im Domain Driven Design vorgesehen ist.

Die richtigen Tools sind Produktivitätsbooster

Auch wenn sich durch die Auswahl der richtigen Testarten und der richtigen Softwarearchitektur der Aufwand für Aufgaben wie Mocking und Fixture-Erstellung erheblich reduzieren lässt, sind sie dennoch weiterhin einer der Hauptbestandteile des Softwaretestings. Daher ist es wichtig, zu Beginn eine passende Tool-Infrastruktur aufzusetzen. So empfiehlt es sich beispielsweise, Fixtures nicht manuell zu erstellen und per Copy & Paste zwischen Tests zu replizieren, sondern sie dynamisch mit Fixture Factories zu erzeugen, welche für alle notwendigen, aber irrelevanten Informationen der Fixtures Default-Werte haben. Auch bei Mocking-Tools gibt es mittlerweile große Unterschiede. So kann es in Microservice-Architekturen beispielsweise sinnvoll sein, die REST-Calls zwischen APIs mit Contract Tests anstelle von herkömmlichen Mocks zu simulieren. Zum Mocken populärer öffentlicher APIs, wie beispielsweise der großen Cloud-Provider, gibt es hingegen häufig Mock SDKs, die sich (aus der Sicht des Verwenders) nicht nur identisch zu den echten SDKs verhalten, sondern die im Anschluss auch einfach zu validieren sind.

Der richtige Umgang mit Legacy-Software

All dies ist natürlich nur möglich, wenn eine Teststrategie bereits vor der Entwicklung der Software erstellt wird. Aus diesem Grund wird empfohlen, Tests bereits vor der eigentlichen Implementierung des Programm-Codes zu schreiben, denn nur so kann sichergestellt werden, dass der Code mit allen Edge-Cases getestet werden kann. Wurde dies nicht gemacht, bleibt häufig nur die Option, Software ausschließlich über Integrations- und Funktionstests zu testen. Hier sind Teams, welche auf Microservices setzen, eindeutig im Vorteil. Zum einen können sie Legacy-Services sehr einfach über ihre API testen, zum anderen ist es viel leichter, Legacy-Services zu ersetzen. Doch auch für Teams mit großen, monolithischen Legacy-Systemen ist das Testen möglich. Hier empfiehlt sich die Auswahl eines modernen Funktionstest-Frameworks wie beispielsweise Cypress. Langfristig sollte der Code natürlich dennoch refaktoriert werden, beispielsweise durch den Rewrite einzelner Komponenten in Microservices, die dann nach TDD bereits vorab getestet werden können.

Fazit

Bei Softwaretesting geht es, wie so häufig, um Qualität statt Quantität. Während falsche Tests schnell geschrieben sind und langfristig zu mehr Problemen als Mehrwert führen, können durch den Einsatz der richtigen Tools und die frühzeitige Überlegung, wie eine testbare Softwarearchitektur aussehen kann, aussagekräftige und wartungsarme Tests implementiert werden. Hierfür ist es jedoch notwendig, das Testen als bewusste Implementierungs- und Designentscheidung zu verstehen und ihm dementsprechende Bedeutung beizumessen. Wird dies nicht gemacht, wird die Implementierung automatisierter Tests zum ungeliebten Zeitfresser, der am Ende der Entwicklung stiefmütterlich umgesetzt wird und in wenig Business-Value resultiert. Dies führt dazu, dass die Softwaretestung noch weiter vernachlässigt wird und am Ende alles von manuellen Tests abhängt. Diese sind jedoch nicht nur ineffizient, sondern auch viel zu langsam - und somit der Killer für jede Innovation.

Share this article!

NEXODE CONSULTING GmbH

OBERWALLSTRAßE 6

10117 BERLIN