Beim Testen von Software wird unter anderem zwischen Black-Box- und White-Box-Tests unterschieden.
Black-Box-Tests überprüfen das Verhalten eines Systems, ohne dessen innere Implementierung zu kennen. White-Box-Tests berücksichtigen dagegen auch die interne Struktur, beispielsweise um bestimmte Programmabläufe gezielt zu überprüfen.
Tests verfolgen unterschiedliche Ziele: Sie können überprüfen, ob ein System die Anforderungen erfüllt, Fehler aufdecken oder sicherstellen helfen, dass bereits funktionierende Teile nach einer Änderung weiterhin funktionieren.
Dabei ist auch die Testabdeckung wichtig. Sie gibt beispielsweise an, welcher Anteil des Codes beim Testen ausgeführt wurde. Eine hohe Codeabdeckung allein bedeutet allerdings noch nicht, dass gut getestet wurde. Denn selbst vollständig durchlaufener Code kann Fehler enthalten, die durch die Tests nicht erkannt werden.
Tests können Fehler nachweisen, aber keine vollständige Fehlerfreiheit garantieren.
Die wichtigste Aufgabe von Tests ist deshalb, begründetes Vertrauen in das Funktionieren eines Systems zu schaffen.
In historisch gewachsenen Softwaresystemen fehlen häufig automatisierte Tests oder es sind nur wenige vorhanden.
Dadurch können sich bei Änderungen unbemerkt Fehler einschleichen. Entwickler wissen nicht zuverlässig, welche anderen Funktionen durch ihre Änderungen betroffen sein könnten. Mit der Zeit trauen sie sich immer weniger, bestimmte Teile des Systems anzufassen.
Aber auch vorhandene Tests können problematisch sein.
Unzuverlässige Tests schlagen manchmal fehl und bestehen ein anderes Mal, obwohl sich am getesteten Code nichts geändert hat. Werden solche Fehlalarme zur Gewohnheit, verlieren Entwickler das Vertrauen in die Tests und ignorieren sie möglicherweise irgendwann.
Komplizierte Tests können wiederum so schwer verständlich sein, dass es viel Aufwand erfordert, ihre Fehlerursachen zu finden oder sie an Änderungen anzupassen.
Gute automatisierte Tests sollten deshalb möglichst:
Eine bewährte Vorgehensweise ist das Test-Driven Development (TDD). Dabei wird ein Test vor dem zugehörigen Programmcode geschrieben. Das kann helfen, Anforderungen frühzeitig zu konkretisieren und den Code von Anfang an testbar zu gestalten.
Zuverlässige Tests wirken wie das Sicherheitsnetz eines Seiltänzers: Wenn etwas schiefgeht, können sie verhindern, dass daraus ein größerer Schaden entsteht.
Bei Änderungen an der Software schlagen Tests Alarm, wenn sie unerwartetes Verhalten erkennen. Dadurch gewinnen Entwickler das Vertrauen, auch ältere oder komplizierte Teile eines Systems wieder zu verändern.
Das erleichtert die Weiterentwicklung bestehender Software erheblich.
Tests helfen außerdem dabei, Fehler gezielt nachzustellen und zu analysieren. Wird ein Fehler durch einen automatisierten Test reproduziert, lässt sich nach seiner Behebung überprüfen, ob der Test nun erfolgreich durchläuft.
Der Test kann anschließend dauerhaft bestehen bleiben und vor einem erneuten Auftreten desselben Fehlers warnen.
Das kann:
Gute Tests sparen somit nicht nur Zeit bei der Fehlersuche, sondern erleichtern vor allem zukünftige Änderungen.
Bei bestehenden Systemen ist es häufig weder sinnvoll noch wirtschaftlich, sofort für den gesamten Code Tests nachzurüsten.
Stattdessen sollten zunächst diejenigen Bereiche untersucht werden, die besonders kritisch sind, häufig verändert werden oder in der Vergangenheit viele Fehler verursacht haben.
Anschließend können gezielt automatisierte Tests ergänzt werden, um das bestehende Verhalten abzusichern.
Dabei lassen sich auch vorhandene Tests verbessern: Unzuverlässige Tests können stabilisiert, komplizierte Tests vereinfacht und Lücken in der Testabdeckung geschlossen werden.
Besonders wichtig ist, dass Tests relevante Risiken abdecken, statt lediglich möglichst viel Code auszuführen.
So entsteht Schritt für Schritt ein zuverlässigeres Sicherheitsnetz für die Weiterentwicklung des Systems.
Tests müssen sich mit dem System weiterentwickeln.
Wenn neue Funktionen hinzukommen, müssen diese auch getestet werden. Ändern sich bestehende Anforderungen, müssen gegebenenfalls auch die zugehörigen Tests angepasst werden. Und wenn Funktionen entfallen, können überflüssig gewordene Tests entfernt werden.
Dabei ist wichtig, nicht jeden fehlgeschlagenen Test einfach an den geänderten Code anzupassen. Denn ein fehlgeschlagener Test kann auf einen unbeabsichtigten Fehler hinweisen.
Tests und Code müssen gemeinsam gepflegt werden, damit die Tests auch langfristig verlässliche Aussagen über das Verhalten des Systems ermöglichen.
Geeignete Tests allein reicht nicht aus, um ein erfolgreiches Softwaresystem zu entwickeln und weiterzuentwickeln. Ebenso wichtig sind klare Anforderungen und eine gute Softwarearchitektur.
Anforderungen beschreiben, was das System leisten muss und welche Eigenschaften es besitzen soll. Sie bilden damit eine wichtige Grundlage für Architekturentscheidungen und die Bewertung des Systems.
Eine gute Softwarearchitektur strukturiert ein System in Teile mit klaren Aufgaben und bewusst gestalteten Abhängigkeiten, die zusammenarbeiten, um die Anforderungen an das System zu erfüllen.
Anforderungen, Softwarearchitektur und Tests ergänzen sich damit gegenseitig und tragen gemeinsam zur Qualität und langfristigen Änderbarkeit eines Systems bei.
Mehr darüber erfahren Sie in meinen Artikeln: