16. August 2021

Tests gehören auch dazu! – Expedition in die Pyramide, die Test-Pyramide.

Heute begeben sich Alex und Matthias mal wieder in den Dschungel der Software-Qualität und erkunden mit dir zusammen die Testpyramide. Warum wird das ganze immer wieder als Pyramide dargestellt? Was bedeutet das für dich als Software-Entwicklerin? Wie beeinflusst sie deine tägliche Arbeit?

Diese und weitere Fragen wollen die Beiden in dieser Folge beantworten. Am Ende dieser Folge wirst du dich deutlich leichter in diesem Dschungel bewegen können.

Bild auf dem Logo der Folge von Simon Berger auf Pixabay

Macht der Craft
Macht der Craft
Tests gehören auch dazu! - Expedition in die Pyramide, die Test-Pyramide.
/

Alex: [00:00:03.37] Herzlich willkommen zu unsere nächste Folge der Macht der Craft mit mir, der Alex

Matthias: [00:00:08.05] Und natürlich auch mit mir den Matthias. Im ersten Teil unserer Serie haben wir uns über automatisierte und manuelle Tests unterhalten.

Alex: [00:00:15.91] Heute wollen wir uns die sogenannte Test Pyramide anschauen.

Matthias: [00:00:20.59] Ihr werdet erfahren, was es damit auf sich hat und wie dieses Bild helfen kann zu verstehen, was in welchem Umfang getestet werden sollte.

Alex: [00:00:28.06] Finde heraus, aus welchen Teile der Pyramide besteht. Damit du entscheiden kannst, wie wichtig diese für die Qualität deiner Software sind.

Matthias: [00:00:36.07] Diese Folge wird dir helfen, die Unterschiede der unterschiedlichen Testdaten zu verstehen und sie gekonnt anzuwenden.

Alex: [00:00:42.28] Lass uns anfangen. Danke fürs Zuhören. Lerne fleißig und habe Spaß dabei.

off: [00:00:48.78] Die Macht der Craft arbeitet für dich.

Alex: [00:00:54.40] Okay, nehmen wir uns als erstes mal: Was ist überhaupt die Testpyramide? Die Testpyramide ist ein Vorschlag, eine Methode, eine Möglichkeit die Test-Strategie aufzubauen, so dass wir eine gute Abdeckung haben, dass wir alles Mögliche in unsere Anwendung vorab getestet haben. Es hat sehr viel mit automatisierte Tests zu tun, aber nicht ausschließlich. Da sind natürlich auch die manuelle Tests berücksichtigt. Automatisierung haben wir das letzte Mal schon gemacht, ist kein Ersatz für manuelle Tests, sondern muss dazu dienen, die manuelle Tests anders zu gestalten. Aber brauchen tun wir sie trotzdem? Du konntest uns jetzt – Matthias – erzählen: Wie ist diese Pyramide aufgebaut?

Matthias: [00:01:45.71] Ja, ganz unten in dieser Pyramide. Also was eher bei so einer Pyramide des Breiteste ist, haben wir also ein relativ breites Fundament an sogenannten Micro- oder Unit-Tests, die Einzelteile der Anwendung also auf Klassen Ebene z.B. runtergebrochen, testen. Und wie bei so einer Pyramide üblich, wird die nach oben hin schmaler, was in diesem Bild letztlich bedeutet, dass wir weniger Tests bekommen, je höher wir diese Pyramide hoch wandern. Und wir haben da eben ganz unten die Unit-Tests, dann kommen Integrations Tests, darüber End-To-End-Tests und ganz oben in der Spitze die manuellen Tests. Je höher ich komme, desto länger werden einzelne Tests brauchen und ich werde weniger davon haben. Das kann man jetzt mal so ganz grob sagen.

Alex: [00:02:45.42] Ich fasse mal kurz zusammen: Die Pyramide ist folgenderweise aufgebaut von unten nach oben Micro- oder Unit-Tests, dann Integrations-Tests, dann End-To-End-Tests und ganz oben manuelle Tests. Das machen wir so, weil je weiter unten wir uns befinden, desto mehr Tests davon haben wollen und desto schneller muss die ausgeführt werden können.

Matthias: [00:03:12.96] Genau

Alex: [00:03:12.96] Ja, das heißt Unit-Test muss ein ruckzuck durchlaufen. Integrations-Test nehmen ein bisschen mehr Test und die Test, die am meisten Zeit brauchen um ausgeführt zu werden, sind normalerweise die manuelle Tests. Unten ist schneller als oben und unten wollen wir mehr davon haben als oben. Es heißt nicht, wir machen keine manuelle Tests, sondern wir versuchen mit Unit-Tests und mit Integrations-Tests, die automatisiert laufen so viel wie möglich schon abzudecken, dass wir die manuelle Test z.B. explorativ durchführen können.

Matthias: [00:03:52.80] Ja, also man was man vielleicht noch hinzufügen könnte ist, dass je weiter ich auch die Pyramide nach oben gehe, desto weniger Teile meines Systems sind gemockt oder gefaked oder sonst irgendetwas. Also ganz unten im Unit-Test-Bereich möchte ich eigentlich alles wegmocken, was nicht Teil der Klasse ist, nicht gerade teste und je höher ich komm, desto weniger Teile sind gemockt und ganz oben, also bei manuellen Testen ist meistens alles wirklich echt. Also das gesamte System ist da, es ist hochgefahren sind sowohl Frontend als auch Backend da und man geht eben manuell durch die gesamte Anwendung. Jetzt zum Beispiel auf Integrations-Test Ebene möchte ich halt die Klassen, die aktiv miteinander interagieren, die möchte ich nicht mocken. Aber Dritt-Systeme wie Datenbank oder externe Services, mit denen ich spreche, die können durchaus noch gemockt sein an der Stelle. Das könnte man jetzt noch so hinzufügen.

Alex: [00:04:59.41] Okay, ja, interessant. Und was hältst du vom statischen Code Analysen? Sind sie ein Teil unserer Test Strategie, weil in die Testpyramide finden die erst einmal kein Platz?

Matthias: [00:05:11.78] Hm, also ich würde sie als Teil einer Entwicklungsstrategie auf jeden Fall sehen. Test Strategie? Jein. Also ich würde sie für mich persönlich eine Stufe sogar davor sehen. Also ich will eigentlich erst Tests ausführen, wenn ich sicher bin, dass ich durch statische Code Analyse keine offenkundigen Probleme mehr im Code habe.

Alex: [00:05:36.18] Was ist statische Code Analyse, Matthias?

Matthias: [00:05:40.47] Also mit einer statischen Code Analyse hat man halt die Möglichkeit, so grundsätzlich bekannte Probleme, die man so in den Code bringen kann, frühzeitig zu erkennen, bevor sie zu einem Problem führen. Ganz einfaches Beispiel wenn man an einer Member Variablen von einer Klasse, wenn man nicht die Sichtbarkeit definiert. Oder man macht es public. Oder sonst irgendwas. Also ein Public Field wird dir von den meisten statischen Code Analysen erstmal als Problem markiert, weil es quasi sicherer ist zu sagen ich hab ein privates Feld, des über, wenns manipuliert werden muss überhaupt, das über einen Setter passiert und nicht direkt über einen public Field. Sowas wäre so ein klassisches Beispiel.

Alex: [00:06:28.00] Also geht es um um statische Analyse, eher um Qualitätsmerkmale, die der Code an sich nicht die Funktionalität, die wir erreichen wollen, sondern der Code an sich hat oder nicht hat. Und geht es darum, diese mögliche Probleme von vornherein zu finden und zu beheben? Kann man das so sagen?

Matthias: [00:06:50.83] Würde ich so sagen. Genau. Also es soll im Prinzip auch die die Wartbarkeit, aber halt auf einer anderen Ebene sicherstellen.

Alex: [00:06:59.83] Für mich ist Teil der Qualitäts Strategie auf jeden Fall. Es ist ein Thema, die wir auch mit berücksichtigen müssen. Meiner Meinung nach, wenn wir entwickeln, dass wir Tools zur statische Code Analyse verwenden und auch die Meldungen, die wir da bekommen, berücksichtigen. Wir müssen nicht immer alle Meldungen entfernen, alle Probleme lösen. Wir müssen uns aber nur bewusst sein, dass sie da sind.

Matthias: [00:07:24.90] Genau, ja.

Alex: [00:07:25.54] Und Alles, was wir mit weniger Aufwand wegmachen können, machen wir eh weg. Also das übliche Abwägen, Wirtschaftlichkeit gegen mögliche zukünftige Probleme. Das ist für mich auch ein wichtiges Thema. Die auf irgendeine Art und Weise auch mit dem, mit dem Tests da mit hängt vielleicht, wie du gesagt hast, ein bisschen weiter unten, bevor wir mit die ganze Test-Strategie loslegen. Aber das da sein sollte und dazugehört.

Matthias: [00:07:52.52] Es ist auf jeden Fall ein sehr wertvolles Tool, das ich nicht missen möchte. Und wie gesagt, zu einer Entwicklungsstrategie oder wie du vorhin genannt hast Qualität Strategie würde ich es auf jeden Fall mit reinzählen. Bei Tests – Ja gut, ich meine, wir haben ihn in der ganzen in unserer Branche allgemein, also wenn man über Tests redet, hört man ja auch unterschiedlichste Begrifflichkeiten für unterschiedliche Arten von Tests. Fehlt irgendwo eh so eine strikte Definition.

Alex: [00:08:25.39] Okay, ich fasse nochmal ganz kurz zusammen: Unten schnell, oben langsamer, unten mehr davon, oben weniger davon und die Reihenfolge von unten nach oben waren Unit, Integration, End-To-End, manuell.

Matthias: [00:08:43.45] Exakt.

Alex: [00:08:44.29] Haben wir! Okay, dann kümmern wir uns um die einzelne Schritte. Fangen wir an mit die Micro-Test- oder Unit-Test-Ebene.

Matthias: [00:08:55.93] Also dann würde ich sagen besprechen wir erst mal was ist denn überhaupt eine Unit? Ich persönlich für mich sag an Unit ist zu die kleinstmögliche Einheit in die ich ein Software System runterbrechen kann. Das kann wenn man objektorientiert arbeitet eine Klasse sein kann, wenn man funktional arbeiten Funktion sein, aber auf jeden Fall das kleinste in sich geschlossene Teil eines Systems ist für mich persönlich ein Unit. Wie siehst du das?

Alex: [00:09:28.06] Ja ähnlich. Die Unit-Test oder Micro-Tests, da teste ich – wichtig – isoliert eine Funktion oder eine Methode oder sogar bis zu eine Klasse, aber in sich geschlossen ohne die Abhängigkeiten auf andere Teile der Anwendung oder gar auf externe Systeme wie die Datenbank oder Dateisystem oder was auch immer, so, die spielen da keine Rolle, sondern ich teste nur den Code, die ich gerade in diese Klasse, oder in diese Funktion implementiert habe, dass der tut, was es tun soll. Dass er sich verhaltensmäßig verhaltet sozusagen. Wenn ich X reinkomme, kommt Y raus und das weiß ich mit Sicherheit. Also schon auf eine sehr niedrige Ebene, sehr technische Ebene. Wir prüfen keine fachliche Konzepte in dem Sinne, sondern eher nur die technische Korrektheit des Codes, sofern wir es testen können.

Matthias: [00:10:32.29] Genau. Und jetzt mag sich so mancher fragen: Ja, warum ist es so wichtig, dass man die Sachen isoliert testet, also ohne die Teile nebendran? Also ich meine, ein ganz klarer Punkt ist die Geschwindigkeit. Weil viel weniger Sachen ich für den Test brauche, desto schneller geht’s letztendlich. Und man kann sich überlegen, wenn man jetzt so etwas wie eine Datenbank für den Test braucht, dann dauert es, bis die Datenbank da ist, bis sie vorbereitet ist, bis die Daten drinnen sind. Das braucht ja alles Zeit und das möchte ich auf der untersten Ebene, weil da gehts wirklich um Geschwindigkeit. Ich will möglichst schnell feststellen, wenn irgendwas kaputt ist, deswegen zu wenig Abhängigkeiten wie möglich. Passt. Aber es gibt auch noch einen anderen Punkt. Und zwar möchte ich nicht, dass mein Test von meiner Klasse A rot wird, weil sich in Klasse B, die ich verwende etwas geändert hat oder da ein Fehler rein programmiert wurde, ein Bug. Das da soll dann der Unit-Test von Klasse B rot werden und nicht der von Klasse A. Weil das führt auch einfach dazu, dass ich schneller an der Stelle im Code bin, die das Problem verursacht.

Alex: [00:11:52.75] Also heißt, wenn Klasse B ein Fehler hat, sollen die Tests in Klasse B rot werden. Damit ich weiß in Klasse B ist ein Problem und nicht überall suchen muss.

Matthias: [00:12:02.92] Genau. Und Klasse A verhält sich ja nach wie vor korrekt. Wenn Klasse B sich richtig verhalten würde. Und wenn ich die Klasse B mocke, dann gebe ich ja quasi vor, wie sich Klasse B verhält. Und dann ist es okay. Vor allem wenn ich halt dann gegen Interfaces arbeite, dann sowieso

Alex: [00:12:23.74] Als erstes, wenn du alles abkapselt. Wie du gesagt hast, wenn du die Abhängigkeiten mockst, dann gibst du vor, wie sie sich verhalten. Das bedeutet aber nicht, dass in Wirklichkeit sie sich auch so verhalten.

Matthias: [00:12:35.44] Das ist korrekt.

Alex: [00:12:37.78] Da sehe ich eine gewisse Diskrepanz. Und deswegen müssen eventuell Integrations-Tests da wichtiger als Unit-Tests. Was hältst du davon?

Matthias: [00:12:47.44] Naja, sie sind insofern wichtiger, dass sie dann das Zusammenspiel, dieser Objekte in Wirklichkeit testen. Und auf Unit-Test-Ebene will ich aber, wie vorhin definiert, isoliert. Also ich habe eine Klasse A, die ein erwartetes Verhalten, auf das ich gerade testen möchte, die benutzt Klasse B und damit Klasse A so funktioniert, wie sie eben funktioniert, verhält sich Klasse B so wie Klasse A das halt erwartet. Wenn es in Realität nicht so ist, muss das natürlich über einen drüber liegenden Test, eben auf Integrations Ebene oder spätestens auf End-To-End-Ebene sichergestellt werden, dass dieses vorgegebene Verhalten auch korrekt ist. Das ist vollkommen richtig. Also wieder diese ganzen Test-Arten ergänzen sich in meinen Augen natürlich irgendwo. Man hat natürlich einen Trade off dafür, dass man die unterste Ebene seiner Test Pyramide, wo ich ja ganz viele haben will, dass die schnell ist. Da muss ich natürlich irgendein Trade off haben und die wirkliche Sicherstellung, dass auch die Klassen untereinander richtig zusammenspielen, die braucht halt einfach mehr Zeit, weil, wie vorhin schon erwähnt, ich brauch ja dann mehr Sachen, die ich orchestrieren muss, damit ich diese Tests ausführen kann. Und das ist dann da der Trade off. Ich brauch mehr Zeit für die Ausführung der Tests, aber ich stelle auch mehr sicher, das funktioniert.

Alex: [00:14:19.40] Okay, wir haben jetzt sehr viel über Unit sinniert. Unit-Test, Micro-Test. Wir wollen isoliert testen. Wir wollen ohne Abhängigkeit testen. Aber wir haben das Problem. Es gibt Klassen. Es gibt Funktionen. Die haben Abhängigkeiten, wenn ich sie ohne diese Abhängigkeiten testen will. Wie kann ich das machen?

Matthias: [00:14:40.10] Spontan fallen mir da zwei Wege ein. Oder was heißt zwei Wege, sondern aber zwei Sachen, die hilfreich sein können. Und das eine ist, dass man Wege findet, wie man Abhängigkeiten in Klassen rein bekommt. Also idealerweise, für mich persönlich wäre das eine Constructor-Injection. Das heißt, bei der Erstellung meines Objekts gebe ich die Abhängigkeiten mit in den Konstruktor rein. Es gäbe da noch andere Möglichkeiten. Es gibt dann sogenannte Setter-Injection, also dass ich quasi Abhängigkeiten über einen Setter in die Klasse rein bekomm. Da gibt’s dann auch unterschiedlichste Ansichten, was da jetzt besser ist oder wann man was verwenden sollte. Ich hab mal gehört und ich fand die Erklärung eigentlich ganz gut, dass man Sachen, die zwingend erforderlich sind, damit eine Klasse funktioniert, über Konstruktor auf jeden Fall reingeben sollte. Und Sachen, die optional sind, können über setter reingehen, weil dann spare ich mir zum Beispiel die Tatsache, dass ich vielleicht mehrere Konstruktoren brauch, weil ich brauch den Konstruktor für alle notwendigen Sachen und ich brauche einen Konstruktor für alle optionalen Kombinationen dann auch noch. Das heißt, da macht dann eventuell ne Setter-Injection Sinn. Und der andere große Punkt ist halt Interfaces. Also wenn ich Interfaces habe, die definieren, wie sich die Klassen verhalten, dann hilft das auch beim Mocken, Faken, weil ich brauch sogar am Ende des Tages nicht mal eine eine implementierte fertige Klasse, sondern mir reicht theoretisch das Interface und ich kann schon mal damit arbeiten. Also dazu kann man z.B. auch wenn parallel entwickelt wird, dann können die Entwickler unter sich können ihre Interfaces im Prinzip einfach erst mal austauschen. Und ohne dass da wirklich Logik das man implementiert ist, kann der jeweilig andere mit dem Interface seine Tests für seine Klasse schreiben.

Alex: [00:16:55.10] Okay, klingt, joa, gut. Interessant.

Matthias: [00:16:57.68] Interessant

Alex: [00:16:57.68] ja. Also Interfaces, Constructor Injection sind zwei sehr gute Möglichkeiten, Abhängigkeiten abzuwenden, in dem man dann dafür die entsprechende Mocks oder Fakes verwendet. Sehr gut, sehr klar. Aber wir wollen vielleicht jetzt weiter zu der zweite Ebene uns begeben der Pyramide. Wir klettern der Pyramide hinauf und das nächste, was wir da finden sind die Integrations-Tests. Und unter Integrations Tests wird eine ganze Menge an unterschiedliche Tests gemeint, meiner Meinung nach. Wie siehst du das?

Matthias: [00:17:42.53] Wie auch schon erwähnt, es gibt für die meisten Testdaten eh auch unterschiedliche Namen. Viele Leute meinen anderen Sachen, ich würde jetzt mal ganz allgemein sagen auf der Ebene will ich alles das, was ich auf der Unit-Test-Ebene. Also alles, was wir gerade eben über Constructor Injection rein gegeben haben in unsere – in unsere Klasse und vorher gemockt haben, würde ich sagen: Auf dieser Ebene wird das nicht mehr gemockt, sondern da hab ich die echten Klassen, die auch verwendet werden mit ihren Implementierungen. Also da könnte ich dann auch mit einem mit einem reinen Interfaces z.B. dann nicht mehr unbedingt arbeiten. Da muss es dann zu einem Interface auch eine implementierte Klasse geben.

Alex: [00:18:30.95] Also einen Integrations-Test, testen wir Einheiten, die in unser System laufen und zwar die Zusammenarbeit zwischen den unterschiedlichen Einheiten, unterschiedliche Klassen, unterschiedliche Funktionen. Das heißt, wir entfernen nicht alle Abhängigkeiten, oder nicht mehr alle Abhängigkeiten, sondern wie du glaub ich am Anfang gesagt hast externe Abhängigkeiten, also Abhängigkeiten, die nicht innerhalb unseren System laufen oder implementiert sind. Die mocken wir weiterhin, aber wenn wir drei Klassen haben, die miteinander arbeiten in dieser Integration Test würden die echte drei Klassen, dassein und würden wir prüfen, dass die Zusammenarbeit zwischen die drei Klassen auch so stattfindet, wie wir es uns vorstellen. Wie wir es definiert haben.

Matthias: [00:19:18.89] Genau. Zu einem gewissen Grad kann man aussagen auf der Integrations Ebene beweisen wir die Annahmen oder dass die Annahmen, die wir auf Unit-Ebene getroffen haben, richtig sind. Wir hatten ja vorhin auch schon mal ganz kurz. Oder etwas länger wie man will. Das wir ja auf der Unit-Ebene, sagen wir ja, wie sich unsere Abhängigkeiten verhalten sollen, damit unsere Klasse richtig funktioniert. Und auf der Integrations-Ebene kann man auch dann sagen: wir verifizieren, dass diese Annahmen richtig sind.

Alex: [00:19:53.36] Okay.

Matthias: [00:19:54.53] Weil wenn dort dann was rot wird, ne, dann – Und alle meine Unit-Tests sind grün, dann kann es mit ziemlicher Sicherheit sagen, dass in einem meiner Uni Test ne falsche Annahme getroffen wurde.

Alex: [00:20:07.49] Ja, ok. Ich sehe im Bereich Integration erstmal eine Differenzierung. Die eine wäre Integrations-Tests in denen ich eben diese Mocks auflöse. Die andere wäre um, und sie schließen sich nicht gegenseitig aus, die zweite wäre das Thema Gurken. Fällt das Thema Gurken, Cucumber, Gherkin für dich in der Integration, in Integrations Tests. Wie siehst du das?

Matthias: [00:20:44.68] Ja, es stimmt, es kommt ein bisschen drauf an. Also ich glaube du also du kannst wahrscheinlich mit Gherkin ähnlichen Test-Framework, sag ich mal. Also Cucumber und wie sie alle heißen. Da gibt’s ja in unterschiedlichsten Programmiersprachen Implementierungen dafür. Kannst du glaub ich beides machen. Sowohl Integration als auch End to end. Ich persönlich würde sie aber tatsächlich eher zu Integrations Zwecken einsetzen. Also ich würde nach wie vor Dritt-Systeme weg mocken. Ich würde nach wie vor Datenbanken weg mocken, einfach weil es nochmal ein bisschen mehr Geschwindigkeit gibt und des eh Sachen sind die meines Erachtens ja. Also eine Datenbank verhält sich halt wie eine Datenbank. Ich frage Daten an, ich bekomme Daten zurück

Alex: [00:21:36.40] Und ich gehe davon aus, wenn wir eine Datenbank brauchen und die Datenbank nicht da ist, da haben wir ganz andere Probleme als …

Matthias: [00:21:41.68] Genau.

Alex: [00:21:42.67] Zu wissen ob Klasse A und Klasse B gut miteinander arbeitet.

Matthias: [00:21:45.97] Genau. Also, weil wenn ich einen Fehler in meinem SQL Statement hab, dann ja. Dann ist also so ein SQL-Statement sollte man dann eventuell auch mal auf der Datenbank ausgeführt haben und sehen obs passt, aber das ist wieder eine ganz andere Diskussion dann. Also wie gesagt, ich find auf der Ebene kann man solche Systeme noch gut und gerne weg mocken. Und ich würds auch tun.

Alex: [00:22:07.30] Wie siehst du das Thema Akzeptanz Tests?

Matthias: [00:22:12.75] Ja, das ist halt auch in meinen Augen einfach nur ein anderer Name dafür. Also, ja. Also ich meine Akzeptanz-Test, das kann für mich ein Feature-File sein, wo aus fachlicher Sicht beschrieben ist, wie ja eine bestimmte Funktionalität in dem System erwartet wird. Und wenn der grün ist, dann sollte gegeben sein, dass akzeptiert wird, das Feature. Also Akzeptanz erlangt. Aber wie gesagt, für mich ist das trotzdem eigentlich nur ein anderes Wort für Integrations-Tests.

Alex: [00:22:45.01] Auf Integration Ebene bewegen wir uns. Wir prüfen eher Akzeptanz-Kriterien. Wir prüfen, wie die Systeme miteinander arbeiten. Eine komplett andere Ebene als Micro- oder Unit-Tests, die aber sich ergänzen, ne? So wie du gesagt hast. Die Annahmen, die wir bei Micro oder Uni Test treffen, prüfen wir in der Integration, dass es tatsächlich des so ist zum Beispiel.

Matthias: [00:23:09.96] Und was man halt auch sagen kann: auf der Ebene will ich halt Features sicherstellen. Also dass zum Beispiel jetzt mal ganz blöd gesagt bei einer Online Anwendung ich etwas in den Warenkorb legen kann im Online-Shop. Das kann so ein Feature sein, es sich auf der Ebene sicherstellen will, dass alle Klassen, die da zusammenarbeiten müssen also Produkt, ein Warenkorb, dass das einfach zusammenspielen kann.

Alex: [00:23:38.82] Hmm, ok, macht Sinn. Da findest du wir sollten der nächste Ebene in unsere Treppchen gehen.

Matthias: [00:23:46.32] Das können wir gern machen. Also ich glaub, ich hab alles gesagt zur Integration, was ich loswerden wollte.

Alex: [00:23:53.13] Okay. Dann die nächste Ebene in der Test Pyramide sind End-To-End-Test oder UI-Tests. Test, die eigentlich die ganze Anwendung an sich betreffen. Oder die gesamte Anwendung testen. Ich sehe die ja, sag ma mal so, ich finde die nicht so schön aus zwei Gründe. Die erste ist das üblicherweise End-to-End oder Oberflächen Test deutlich länger dauern als Integration- oder Micro-Tests. So die kann ich für meinen sofortigen Feedback, die ich brauche während ich entwickle, kann ich sie öfters nicht ausführen, weil da gehen mehrere Minuten teilweise flöten, während die Tests laufen und so viel Zeit auf die Test Ausführung möchte ich nicht warten. Und die zweite ist aber, vor allem was Oberflächen Test betrifft, sie sind nicht besonders stabil. Die Probleme sehe ich bei der Sache deswegen. Ich möchte nicht auf die verzichten. Natürlich nicht, aber ich würde die auf das Minimum reduzieren. Also die Fälle da automatisieren, die notwendig sind. Ja, was ich nicht gerne hätte ist, wenn wir Mikro-Tests und Integrations-Tests 100 habe, dass wir dann 150 UI-Test noch machen müssen. Weil erstens weiß ich nicht, was die dazu beitragen sollen, so viele. Und zweitens ist die… Ausführungszeiten werden total in Keller gehen und drittens jedes mal wo sich die Oberfläche ändert, schlagen meine Tests also sehr schnell fehl, was nicht grundsätzlich schlecht ist, wenn sich etwas geändert hat, aber vor allem bei Oberflächen Test, wenn die Oberfläche sich minimal ändert, passiert oft, dass dann eine ganze Menge an UI-Tests fehlschlagen. Das finde ich immer nicht so, nicht so gut. Was ist deine Meinung dazu?

Matthias: [00:25:57.75] Relativ ähnlich. Also sagen wir so Ich hätte gern gut funktionierende End-to-End / UI-Tests, aber es ist halt wirklich schwierig, weil da so viele Faktoren dann eben zusammenspielen. Weil wenn ich das Ganze halt automatisiert zum Beispiel in meiner, CI-Pipeline machen will, dann muss ich das halt – Also ich muss ja dafür sorgen, dass alle beteiligten Systeme da sind. Ich, wie vorhin auch schon mal erwähnt, ich muss dafür sorgen, dass die Datenbank mit Daten befüllt ist und vor allem mit Daten, die vorbereitet sind, weil ich muss ja in meinen Tests auf Daten testen können. Also ich muss mir sicher sein können, dass wenn ich diese und jene Aktion in meinem System durchführe, dass ich diese und jene Daten dann auch habe und darauf testen zu können. Und das ist eine ja keine triviale Aufgabe, so gut funktionierende End-To-End Test-Suiten hinzubekommen. Also ich glaube, das ist echt schwierig. Anderes Problem, das man natürlich hat es sich muss das, was ich gerade erwähnt habe mit Daten vorbereiten, muss ich im Prinzip eigentlich für jeden End-To-End-Test neu machen. Weil ich muss eigentlich immer ein kontrolliertes System vorweisen können, damit meine Tests auf einer sauberen Datenbank z.B. laufen. Und der Grund dafür ist letztendlich, dass ich sicherstellen muss, dass zum Beispiel die Reihenfolge, in welcher meine Tests ausgeführt werden, die muss egal sein. Es kann nicht sein, dass ein Test, der keine Ahnung wenn wir jetzt, vorhin auf des E-Commerce Beispiel nochmal zurückgehen, der ne Bestellung ausführen soll. Was ja so ein klassische End-To-End-Test wär und er fängt ja an: ich fang ganz vorne an auf der Startseite, geh in irgendne Kategorie, geh auf ein Produkt, legt des in Warenkorb und durchlaufe den gesamten Bestell-Prozess. Und es kann halt jetzt nicht sein, dass ein nach, – also ein anderer Test dann zum Beispiel prüft, keine Ahnung, dass eine Bestellung storniert werden kann, dass der diese Bestellung wieder durchgeführt worden ist, storniert, weil ich muss im Prinzip Unabhängigkeit zwischen diesen Tests gewährleisten und das heißt, ich habe für jeden Test einen unglaublichen Setup-Aufwand und es muss einem einfach klar sein. Und da merkt man dann auch schon, dass da willst du wirklich relativ wenig von haben.

Alex: [00:28:30.91] Also der Aufwand, die vorzubereiten, um sie ordentlich ausführen zu können, ist relativ groß. Zusätzlich zu den Zeiten, die die auch deutlich länger sind als bei den anderen Fällen. Heißt, wir können nicht auf sie komplett verzichten. Wir wollen auch nicht komplett auf sie verzichten. Wir wollen sie aber reduzieren auf einen sinnvollen Maß. Also

Matthias: [00:28:53.92] Genau

Alex: [00:28:53.92] Nur da testen, was wirklich da getestet werden soll. Wie auch immer wir das definieren.

Matthias: [00:29:01.81] Genau. Also das kann jetzt auch, wenn man sich das Beispiel E-Commerce nochmal vor Augen führt, kann das genau dieser eine Testfall sein, den ich erwähnt hab. Wo wirklich von der Startseite bis der Kauf ist abgeschlossen durchgetestet ist. Weil das ist letztendlich das, was ich wirklich sicherstellen will, wenn ich einen Onlineshop betreibe. Ich will das eingekauft werden kann. So, und dieser eine Test, der ist auf jeden Fall essentiell. Aber ob jetzt zum Beispiel zwei Produkte hinzugefügt, die Anzahl erhöht, davon dann wieder eins entfernt werden kann und dass danach der Warenkorb stimmt, des ist mit Sicherheit wichtig, aber ich finde, das ist etwas, das kann ich auf Integrations-Ebene testen, weil da hab ich halt Logik, die muss halt funktionieren – Gut ist, ja aber ich – Was ich sicherstellen will ist, ist, dass ich verkaufen kann.

Alex: [00:29:55.48] Okay, ja. Klingt plausibel. Klingt plausibel.

Matthias: [00:29:58.99] Ich hoffe doch.

Alex: [00:30:01.81] Jawohl. Tut es

Matthias: [00:30:02.34] Ja, und dann haben wir ja noch den letzten Punkt in unserer Pyramide. Das manuelle Testen.

Alex: [00:30:09.43] Wir haben jetzt so viel automatisiert getestet, ja. All diese Tests, die wir bis jetzt besprochen haben, laufen automatisch. Entweder bei der CI oder in bestimmten Läufe. Wie auch immer, laufen aber automatisch. Wofür brauchen wir jetzt noch manuelles Testen?

Matthias: [00:30:27.15] Also ich meine, zum einen sind auch diese automatisierten Tests natürlich keine Garantie, dass alles zu 100 prozent richtig ist, sondern ich glaube, das haben wir auch in der letzten Folge erwähnt. Wir können eigentlich nur gewährleisten, dass das, was wir testen, funktioniert und die Annahmen, die wir in den Tests getroffen haben, dass die halt irgendwo zusammenpassen. Aber es bleiben dann halt noch ganz viele andere Sachen wie Nutzbarkeit der Anwendung, Verständlichkeit, das alles auch irgendwo für einen Benutzer Sinn ergibt. Diese ganzen Sachen, die sind halt sehr schwer automatisch zu testen. Und für sowas finde ich, sind manuelle Tests dann hervorragend geeignet. Ob ich die jetzt selber durchführe, also das mache ich z.B. auch. Wenn ich entwickel, mache ich schon auch manuelle Tests nebenbei um zu gucken, ob alles sich auch im Gesamtsystem so verhält, wie ich mir das vorstelle. Aber es ist natürlich eher die Ausnahme. Also ich verlasse mich in meinem Alltag schon eher auf die automatisierten Tests. Aber spätestens zum Abschluss wird das ganze auch manuell durch getestet

Alex: [00:31:37.69] Und wir brauchen manuelle Tests weiterhin, weil man muss sich eingestehen wir sind nicht unfehlbar. Wir vergessen auch Testfälle zu schreiben, die zwar automatisiert hätten werden können, aber wir haben es einfach vergessen oder haben in dem Moment nicht dran gedacht, haben es einfach nicht gemacht. Und durch manuelles Testen können solche Fehler, die wir vergessen haben, auch aufgedeckt werden. Aber eins und vor allem kann ma manuell machen, was nicht automatisiert werden kann. Und des is die sogenannte exploratives Testen. Wie würden wir exploratives Testen definieren?

Matthias: [00:32:18.76] Also ich versuchs mal wieder mit meinem Beispiel von eben, mit dem E-Commerce. Weil, das was ich vorhin erwähnt habe, was ich nicht in einem End-To-End-Test machen würde, ne. Also das mit ich lege Sachen im Warenkorb verändert die Anzahl, nehmt Sachen raus und so weiter und so fort. Das ist für mich etwas, das kann halt wunderbar explorativ getestet werden, weil das fällst so in die Sachen Benutzbarkeit, wie verhält sich der – ähm die Seite, wenn man so mit ihr umgeht, ja? Also gibt’s irgendnen, also so ein klassisches Beispiel aus dem E-Commerce vielleicht auch wieder ist: Aktualisiert sich die Warenkorb-Anzahl?, also die Sachen, die – man hat ja, oben oft so nen Böppel wo dann irgendwie ne Zahl dran steht, wie viele Sachen hab ich gerade in meinem Warenkorb. Und wenn ich da was aus Warenkorb entfern, will ich halt sicherstellen, dass zum Beispiel die Zahl da oben sich auch anpasst. Sowas wäre für mich also, wenn ich jetzt die Aufgabe hätte, explorativ irgendwas so durch zu testen. Des wären so Sachen, da würde ich dann drauf achten.

Alex: [00:33:17.77] Okay. Also geht es eher um das nicht daran gebunden sein. An irgendein Script A, B und C muss getestet werden, weil wir versuchen, dass diese Tests, die sich immer wiederholen und immer gleich aussehen, durch irgendeine der automatisierte Test abgedeckt ist. Da geht es vielmehr darum, dass der Tester frei von Scripting, frei von Zwänge sich die Anwendung insgesamt anschauen kann und prüfen kann, ob die tatsächlich so bedient werden kann, wie sie es sollte. Das man auch alle Features, die da sein sollten, da sind. Ja,

Matthias: [00:34:01.93] Genau und eine gewisse Kreativität kann man dabei ja auch an den Tag legen. Also schadet

Alex: [00:34:07.66] Mit den Kopf gegen die Tastatur?

Matthias: [00:34:11.92] Naja, das vielleicht jetzt nicht, aber ich meine, wenn man sich dann durch so eine Anwendung bewegt, da fällt einem dann vielleicht mal irgendwie was ein, was man da jetzt mal machen könnte. Und sowas steht ja dann meistens eben net in solchen Test-Plänen. So dieses unerwartete User-Verhalten. Und das kann man damit eventuell, also vor allem wenn man dann mehrere Leute hat, die parallel explorativ testen und auf vollkommen unterschiedliche Art und Weise vielleicht mit deinem System umgehen. Da kann man glaub ich ganz wertvolle Sachen draus ziehen.

Alex: [00:34:44.10] Also die vier Ebenen haben wir jetzt mal ein bisschen erklärt. Es gibt Menschen, die mehr Ebenen haben, Menschen, die vielleicht ein paar weniger Ebenen haben, aber im Großen und Ganzen denke ich, diese vier grobe Ebene der Klassifizierung von Test mussten uns erstmal reichen, denke ich mal. Und damit kann man ja schon sehr, sehr, sehr viel von der Anwendung ordentlich testen und eine wirklich gute Qualität in unsere Produkte reinbringen. Eher ein Teil automatisiert mit Micro- Unit-Tests Integrations-Tests die notwendigen End-To-End und UI-Test und auch natürlich das manuelle Testen explorativ oder nicht explorativ, sei dahingestellt. Jeder wie er es für richtig hält, aber auch diese diese manuelle Tests sind wichtig und relevant. Was ich noch hätte für diese Folge, wäre: Was ist der Unterschied zwischen „Test First“ und „Test Last“? In was unterscheiden sie sich? Warum ist der eine besser als der andere oder ist eine besser als der andere? Ich sage immer, mir ist es egal, ob Ihr „Test First“ oder „Test Last“ arbeitet. Hauptsache es sind Tests da. Wenn ihr den Feld verlässt, aber natürlich hat „Test First“ einige Vorteile, eventuell auch einige Nachteile. Haben wir uns in der letzten Folge schon ein bisschen angeschaut. Hauptsächlich Automatisierung und „Test Last“ hat gegebenenfalls auch Vorteile und Nachteile. Darüber würde ich jetzt noch ein bisschen sinnieren, ein bisschen darüber reden. Warum sollen wir die Test vor dem Code schreiben oder warum wollen wir die Tests nach dem Code schreiben? Was ist besser? Was hilft uns mehr?

Matthias: [00:36:36.37] Also ich bin klarer Verfechter von „Test First“, weil ich der Meinung bin, dass es einfach A kein also wenn man sich mal dran gewöhnt hat kein Problem ist, Tests vorher zu schreiben und ich sehe persönlich – Also mir persönlich fällt kein Nachteil ein, den Test zuerst zu schreiben, außer der einzige Nachteil könnte vielleicht sein, dass ich mir vorher Gedanken drüber mach, wie mein Code sich verhalten soll. Aber das sehe ich persönlich nicht als Nachteil, sondern eher als Vorteil. Und ja, also ich bin da ganz klarer Verfechter und ich meine, man kann, was man jetzt gleich noch sagen kann. Wenn wir jetzt nochmal uns die Pyramide vor Augen führen „Test First“ sehe ich in erster Linie ganz unten auf Unit-Ebene, dass ich „Test First“ als wichtig sogar eigentlich, weil letztendlich ich auf der Ebene des Design insofern steuern kann, dass ich Testbarkeit, Wartbarkeit und so weiter und so fort, im Fokus hab, was letztendlich dazu führt, dass mein Code allgemein besser testbar und wartbar sein wird, weil er ja über Tests entstanden ist. Also das bedeutet nicht, dass das alles automatisch passiert, sondern ich kann natürlich auch zuerst schlechte Tests schreiben, die dazu führen, dass schlechter Code rauskommt, keine Frage. Das ist auch keine Silver Bullet, wie es so schön heißt. Aber in meinen Augen hilft es sehr stark besser testbar und wartbaren Code zu produzieren. Und auf der anderen Seite fällt mir tatsächlich kein Vorteil ein, den ich hätte, wenn ich den Test danach schreib. Da fallen mir persönlich eigentlich nur Nachteile tatsächlich ein.

Alex: [00:38:18.43] Okay.

Matthias: [00:38:18.43] Und einer der größten Nachteile ist der klassische „Wir haben ja keine Zeit“ und die Tests, sind dann das Erste, die hinten runterfallen. So, und Deswegen würde ich persönl-, weil eine Implementierung für die wird dir immer Zeit gegeben werden, wenn du nicht fertig bist mit der Implementierung. Dein Projektleiter, dein PO wird dir die Zeit für die Implementierung geben, weil das ist ja das, was der Kunde braucht am Ende. Aber bei einem Test wird die Diskussion ganz anders verlaufen. Und deswegen -Ich sehe da nur Nachteile.

Alex: [00:38:53.30] Okay, ich sehe – also ich bin auch ein Verfechter von „Test First“, aber nicht immer und nicht überall. Muss nicht sein. Es ist eine Sache der Disziplin, wie du wohl gesagt hast. Also es passiert sehr oft, dass „Test Last“ bedeutet „Test never“. Weil ich schreibe mein Code, dann ist mein Code da und dann gibt es keine Zeit, um die Tests zu schreiben, weil der nächste Issue, des nächste Feature muss schnell, schnell implementiert werden und das ist grundsätzlich schlecht. Das ist aber nicht ein Problem des „Test First“ oder „Test Last“, sondern eher ein Problem der Organisation, in dem Sinne. Was hat „Test Last“ für einen Vorteil für mich? Und zwar ich kann mich auf die Funktionalität, die ich implementieren will, konzentrieren und zwar nicht kleinklein, sondern schon ein bisschen größer, ein bisschen die gesamte Sicht auf die Implementierung haben, kann die Sachen im Groben schon implementieren und dann nachträglich – aber mit nachträglich meine ich sofort danach. Nicht drei Wochen danach, sondern sofort danach. Erst einmal so nen Grund-Code schreiben und dann des mit Tests immer wieder verfeinern. Ich habe aber mit dem Code angefangen, nicht mit dem Test. Deswegen ist für mich „Test Last“ und nicht „Test First“. Hat den Nachteil, aber der Code, die daraus kommt, ist nicht immer so schön zu testen, wie es sein würde, wenn ich die Tests vorher geschrieben hätte. Manchmal ist es ein bisschen schwieriger, die Tests zu schreiben. Aber man muss wie gesagt abwägen. Was für mich wirklich wichtig ist, ist, dass wenn ich sage diese Feature ist fertig, ich als Entwickler Tests habe, die das prüfen, die das beweisen können. Wie du wohl gesagt hast, nicht immer zu 100 prozent, des wird nicht geben, aber soweit wie möglich die Anwendung, die Funktionen, die klar sind, die sind getestet. Egal ob ich die Tests vorher oder nachher geschrieben habe. Das ist für mich das Wichtige.

Matthias: [00:41:01.65] Ja, vielleicht bin ich da ein gebranntes Kind. Keine Ahnung, ich habs schon zu oft erlebt, dass einem eben z.B. wie ichs vorhin auch erwähnt hab die Zeit dann eben einfach nicht gegeben wird, die man bräuchte. Und ja, das Feature ist ja schon fertig, du machst es sonst anderes. Und naja. Aber ja, ich verstehe deine Argumentation. Wie gesagt, ich finde trotzdem, die Gefahr ist halt wirklich da, dass – ja – Tests einfach hinten runterfallen dann.

Alex: [00:41:28.87] Ja, natürlich. Ja, die Gefahr ist immer da. Es bedarf Disziplin. Und wie sagt man so schön: Des Standing zu sagen, die des Features ist nicht fertig, bis die Tests auch fertig sind. Aber ich sehe es wie gesagt, das ist auch eine Sache der Organisation. Wenn die Organisation dir die Freiheit gibt, das so zu machen, dann is – wäre mir egal ob First ob Last. Wenn die Organisation eher nicht so ein agilen Kontext arbeitet, dann würde ich vielleicht ja tatsächlich tendieren auf „schreib ma die Test zuerst“, dann sind sie schon da und die Sache ist gegessen. Wie gesagt, ich finde einfach beide Möglichkeiten nutzen zu können, so wie es für mich gerade am besten ist die richtige Kombination für mich persönlich. Aber ich denke jeder muss da selber halt mal entscheiden. Was du natürlich gesagt hast, stimmt, wenn du „Test First“ machst, sind die Test erstmal schon da musst du dir keine.

Matthias: [00:42:26.40] Niemand – Wird niemand wird die wegwerfen. OK vielleicht zu weit aus dem Fenster gelehnt. Ja nee, aber ja, ich glaube auch wir sind uns schon einig. Das Wichtigste ist, dass am Ende des Tages Tests da sind. Und ich werde auch niemanden verteufeln, der sagt, er macht des im Nachgang. Es ist auch vollkommen okay. Ich persönlich versuche mein Bestes, dass ich meine Tests immer davor schreibe und was ich da vorhin auch noch sagen wollte. Als ich angefangen habe mit ganz unten „Test First“ ist, dass ich natürlich die Tests drüber liegen, also auf Integrationsebene und auch End-To-End-Ebene. Ich schreibe die dann persönlich auch nachträglich. Also es gibt auch Leute, die tatsächlich die zuerst schreiben und die immer wieder ausführen, bis die grün sind. Aber da gehöre ich jetzt tatsächlich auch nicht dazu.

Alex: [00:43:20.07] die Möglichkeit gibt’s natürlich auch, ne. Ist schon mal sehr Integrations Test zu schreiben, die natürlich alle Rot sind, weil keine Anwendung da ist noch

Matthias: [00:43:26.73] Genau, ja

Alex: [00:43:29.43] Und dann Nach und Nach mit Hilfe von die entsprechende Unit-Tests die wir machen z.B. diese Integrations Test Nach und Nach grün zu bekommen.

Matthias: [00:43:38.25] Ja.

Alex: [00:43:38.94] Ich auch eine eine Möglichkeit und man behauptet dadurch braucht man auch weniger Unit-Test. Am Ende des Tages, ich würde mich so Consultant-like verhalten und zwar: „It depends“.

Matthias: [00:43:55.78] „It depends“.

Alex: [00:43:55.78] Hängt davon ab. Wenn ich entwickel, dann je nach Gegebenheit verwende ich mal manchmal „Test First“ oder TDD und manchmal „Test Last“. Hiermit sind wier am Ende von dieser Folge angelangt.

Matthias: [00:44:14.98] Du hast heut einiges gehört über die Ebenen der Test Pyramide und welchen Mehrwert die liefern können, Im Bezug auf Qualität und Stabilität unserer Anwendungen.

Alex: [00:44:24.55] Wir hoffen, dass du eine gute Zeit mit uns hattest. Und wenn du den Podcast genossen hast, denke bitte daran, zu abonnieren, liken und oder es an deine Kollegen weiterzugeben, damit sie auch damit profitieren können.

Matthias: [00:44:38.05] Übrigens haben wir auch unsere Website mittlerweile die Möglichkeit eingerichtet, einen Newsletter zu abonnieren, damit man auch einfach keine Folge mehr verpasst.

Alex: [00:44:47.32] Falls du informiert werden möchtest, einfach auf die Website gehen, um dich dann einschreiben sozusagen. Nächstes Mal kommt die dritte Folge von diese Reihe, wo es ums Testen geht. Mit dem Thema „TDD – Tests Driven Development“, wäre eine tolle Sache, wenn wir uns dort wieder treffen.

Matthias: [00:45:08.20] Vielen Dank, dass du bis zum Ende hiergeblieben bist und bis zum nächsten Mal.

Alex: [00:45:12.94] Wir sehen uns. Danke. Lasst es euch gut gehen.

off: [00:45:15.01] Die Macht der Craft arbeitet für dich.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.