Die Test-Pyramide der Software und warum sie so wichtig ist

Mit steigendem Qualitätsanspruch an Software, ist auch das Bedürfnis größer denn je seine eigene Software zu testen. Früher oder später kommt dadurch jeder Entwickler mit der Test-Pyramide in Berührung ob nun freiwillig oder unfreiwillig.

Immer wieder stelle ich fest, dass das grundlegende Prinzip wie eine Applikation nach diesem Konzept „sinnvoll“ getestet wird, in Gänze ignoriert oder für unwichtig erachtet wird. Häufig sind es immer die selben Gründe: „Es ist aber eine Anforderung“, „Wir können sonst die Funktionalität nicht sicherstellen“ oder der Klassiker: „andere machen es aber auch so“. Da viele Consultants ebenfalls gegen diese Entwurfsmuster verstoßen, ist es oftmals schwierig, andere davon zu überzeugen dieser „Empfehlung“ nicht nachzugehen.

Dieser Artikel soll in Kürze die Test-Pyramide aufzeigen, erklären und veranschaulichen was es bedeutet, wenn explizit oder implizit gegen dieses Designmuster verstoßen wird.

Werfen wir zu erst einen Blick auf die Test-Pyramide:

Darstellung der Test-Pyramide

Unit Test

Auf unterster Ebene finden wir die Unit-Tests, diese Schicht bildet unser Grundgerüst der Pyramide. Idealerweise haben wir für alle Klassen, Methoden, etc. einen adäquaten Unit-Test. Dabei testen wir if-else Abzweigungen sowie Grenzwerte, welche ein mögliches Fehlverhalten unserer Anwendungen erzeugen könnten. Ebenfalls kann auf dieser Schicht auf Mutation-Testing gesetzt werden (mehr dazu in einem anderen Beitrag).

Die Anzahl der Unit-Tests kann schnell in die Tausende gehen, je nach Größe der Anwendung. Ebenfalls ist es ratsam, Tests so weit unten wie möglich anzusiedeln, sprich, ist ein Test umsetzbar als Unit-Test, sollte dieser nach Möglichkeit dort umgesetzt werden. Dadurch können wir diesen in größtmöglicher Isolation ausführen (wie wir links in der Grafik sehen können). Je höher die Isolation, desto niedriger, ist in der Regel, die Ausführungszeit. Erfahrungsgemäß ist es wichtig, dass man als Entwickler frühzeitig erkennt ob ein Stück des Codes korrekt funktioniert oder nicht. Dies kann ebenfalls, je nach Workflow, Zeit und damit zwangsweise Geld sparen.

const doesStringStartsWithHttp = (str: string) => {
  if (str.startsWith("http")) {
    return true;
  }

  return false;
}

Sehen wir uns obiges Codebeispiel einmal genauer an. Sollte ein String mit ‚http‘ beginnen wird ‚true‘ zurückgegeben. Es fällt jedoch auf, dass sollte bspw im String ‚hTTp‘ enthalten sein, die Funktion ‚false‘ zurück liefern wird. Die Funktion ist somit nicht ‚case sensitive‘, sollte dies nicht gewünscht sein, verhält sich unsere Funktion also nicht wie gewünscht.

Exkurs

Angenommen, obiges Fehlverhalten der Funktion wird in einem separaten Test der Applikation, ggf. durch manuelle Tester, gefunden. Es stellt sich nun die Frage, in welcher Schicht wir diese Funktion testen.

Im hypothetischen Entwickler Team scheiden sich die Geister, da es eine Kernkomponente betrifft, pocht eine viel Zahl darauf, dieses Fehlverhalten als Test umzusetzen auf UI Ebene. Ein UI Test ist sehr schnell umgesetzt, kostet jedoch viel Zeit.

Andere im Team, sehen diesen Fall jedoch als Unit Test an, somit können Regression Bugs in dieser Hinsicht vermieden werden und die Entwickler bekommen in Sekunden schnelle Feedback.

In diesem simplen Beispiel ist es recht offensichtlich, dass wir den Test als Unit Test umsetzen wollen und werden.

Integrations-Test

Integration Tests kann es in verschiedenen Farben und Formen geben. Hat man eine Webapplikation, kann es von der Backend Schnittstelle bis hin zur Datenbank gehen. Im Allgemeinen, stellt ein Integration Test sicher, dass diverse Komponenten im Zusammenspiel korrekt funktionieren. Wikipedia schreibt dazu folgendes:

Der Begriff Integrationstest bezeichnet in der Softwareentwicklung eine aufeinander abgestimmte Reihe von Einzeltests, die dazu dienen, verschiedene voneinander abhängige Komponenten eines komplexen Systems im Zusammenspiel miteinander zu testen.

https://de.wikipedia.org/wiki/Integrationstest

Wenn wir nun die Test-Pyramide anschauen, fällt auf, dass wir tendenziell weniger Integrationstests haben werden als Unit Tests. Dies ist in Ordnung und soll unser primäres Ziel sein, da es mehrere bestandene Unit Tests braucht um überhaupt einen Integrationstest haben zu können.

UI Tests

Am Ende der Pyramide finden wir die UI Tests, diese sind im Allgemeinen sehr langsam und können z.T. bis zu Stunden dauern. Die meisten lassen diese Art von Tests einmal in der Nacht laufen und schicken eine Zusammenfassung mit den Testergebnissen an alle relevanten Personen.

In einem UI Test möchte man sicherstellen, dass beispielsweise eine Mobile Applikation oder eine Webapplikation aus der Sicht der Nutzer korrekt funktioniert und sich wie gewünscht verhält.

Sollte auf dieser Ebene ein Test umgesetzt werden, welcher als Unit Test umsetzbar ist, ist die logische Konsequenz, dass viel Zeit und Geld dadurch verloren geht. Da, je nach Häufigkeit und Dauer pro Ausführung, im Schnitt, mehr Zeit vergeht, bis der Entwickler davon erfährt, dass etwas kaputt ist. Selbiges gilt natürlich auf für Integrationstests.

Als kleine Faustregel: Schlägt ein UI Test fehl und es gibt keinerlei Integration oder Unit Test, welcher ebenfalls fehl schlägt, wurde die Test-Pyramide nicht eingehalten.

Exkurs

Zum Abschluss, noch ein kleiner Exkurs, was immer wieder für Diskussion sorgt. Mal angenommen wir entwickeln an einer Webapplikation und es wird für das UI Testing Selenium genutzt. Es kommt nun die Anforderung sicherzustellen, dass ein gewisser API Aufruf getätigt wird, wenn mit dem Mauszeiger über ein Element gefahren wird.

Wo würdest Du den Test implementieren und warum?

Meiner Meinung nach und in Anbetracht der Test-Pyramide, kann und muss es mindestens einen Unit Test für diese Funktionalität geben. Im Rahmen der Integrationstests kann es ebenfalls noch mindestens einen Test geben auch einen, welcher sicherstellt, dass die zu erwartenden Werte der API zurückgeliefert werden. In meinen Augen ist es für einen UI Test irrelevant ob API Aufrufe abgesetzt werden oder nicht. Was zählt ist das User Interface und nicht das System oder das Backend. Angenommen es gäbe eine UI Test dafür, schlägt dieser fehl, geht die große Suche nach dem Fehler los. Ein roter Test soll im Idealfall nicht für eine große Suche sorgen, sondern direkt dem Entwickler mitteilen, wo etwas kaputt ist.

1480cookie-checkDie Test-Pyramide der Software und warum sie so wichtig ist

1 Kommentar zu „Die Test-Pyramide der Software und warum sie so wichtig ist“

  1. Pingback: Wie wertvoll sind Snapshot-Tests? – r3d-soft Blog

Kommentar verfassen