Podcast

Testpyramide oder Testdiamant?

Welche Automatisierungsstrategie passt zu meinem Kontext?

In dieser Episode des INNOQ Podcasts diskutieren Daniel Westheide, Jakob Oswald und Sven Johann über das Für und Wider verschiedener Teststrategien. Ausgehend von einer internen Debatte bei INNOQ gehen die drei der Frage nach, wie viel Gewicht auf Unit Tests, Integrationstests oder exploratives Testen gelegt werden sollte – und was die Domäne damit zu tun hat. Sie sprechen über transaktionale Fallstricke, überflüssige Redundanz durch überambitionierte Testabdeckung und persönliche Erfahrungen aus regulierten Projekten. Am Ende steht kein Dogma, sondern ein differenziertes Bild: zwischen Testpyramide, Testdiamant und gesundem Pragmatismus.
Weitere Episoden anhören

Shownotes & Links

Transkript

Transkript ausklappen / einklappen

Sven Johann Hallo und herzlich willkommen zu einer neuen Episode des INNOQ Podcasts. Heute mit Daniel Westheide und Jakob Oswald. Schon wieder Jakob, du bist ja Dauergast hier. Wir wollten über über die Testpyramide, Testdiamant, manche sagen auch Bienenwabe, so über diese ganzen Abgrenzungen sprechen. Auf jeden Fall erstmal herzlich willkommen ihr beiden. Daniel, stell dich doch mal vor.

Daniel Westheide Ja, hallo, ich bin Daniel, ich bin Senior Consultant bei INNOQ. bin seit 2013 dabei und habe auch schon die ein oder andere Podcast Folge mitgemacht. Auch mit Sven zusammen schon, aber auch schon einige davor.

Sven Johann Ja, Programming as a Theory Building z.B..

Daniel Westheide Genau, genau.

Sven Johann Gut, Jakob.

Jakob Oswald Ja, hi, ich bin Jakob, ich bin Principal Consultant gerade geworden und bin seit 2021 bei INNOQ.

Sven Johann Okay. Testpyramide. Also vielleicht mal so die die ganze Motivation überhaupt für diesen Podcast, wir hatten INNOQ intern eine Diskussion zum Testen. Da ging’s also los ging’s irgendwie mit ich hoffe, ich gebe das richtig wieder, aber macht man jetzt lieber Mockist TDD oder macht man sozusagen Classical TDD? und dann kamen halt so ein paar Aussagen noch dazu zum Thema macht man lieber mehr Integrationstests, also statt Unit Tests, also eher dann so ein Test Testpyramide statt sorry, Testdiamant statt Testpyramide. Und haben einfach gedacht, da machen wir jetzt mal, das müssen wir mal ein bisschen tiefer besprechen und nicht nur in Slack diskutieren. Genau, Testpyramide, legen wir mal dafür damit los. Was ist überhaupt die die Motivation für so eine was ist überhaupt die Testpyramide? Vielleicht fangen wir mal damit an. Wer wer will mal kurz was zur Testpyramide sagen, zur zur zur einfachen Testpyramide, die ja, glaube ich, 28 oder so von Mike Cohn definiert wurde.

Daniel Westheide Ja, kann ich gerne machen. Also vielleicht erstmal zur Motivation, es geht uns ja darum, dass wir Änderungen möglichst schnell auch in Produktion bringen können und dabei aber eben auch die Qualität der Software sicherstellen wollen. Und dafür brauchen wir eben auch eine Test, die schnell ausgeführt werden kann und wartbar ist und möglichst stabil ist und Mike Cohn hat eben als Antwort darauf die Testpyramide vorgeschlagen. Das war, glaube ich, in dem Buch Succeeding with Agile. Und die Testpyramide, die er damals vorgeschlagen hat, die sah eigentlich sehr einfach aus. Auf der obersten Ebene waren die UI Tests, darunter Service Tests und als Basis schließlich die Unit Tests und die Darstellung soll eben verdeutlichen, dass wir sehr wenige UI Tests haben, etwas mehr Service Tests und ganz ganz viele Unit Tests und ja, die Kosten, die mit der Entwicklung und der Wartung dieser Tests verbunden sind, die steigen halt von Unit zu Service UI und aber auch die Ausführungszeiten der Tests. Und die Stabilität nimmt ab, könnte man, glaube ich, tendenziell sagen, dass man also in der UI Ebene eher mal Flaky Tests hat z.B..

Sven Johann Ja, wegen Integration von Umsystem z.B., ne?

Daniel Westheide Genau.

Sven Johann Genau, dann dann gibt’s auch noch dann gibt’s auch noch die erweiterte Testpyramide, also Practical Testing Pyramid, die finde ich eigentlich finde ich eigentlich ganz interessant, die hatte ja auch der war ja bei bei Martin Fowler auf der Webseite hat, ich habe vergessen, wie er heißt, kommt aus Hamburg. Ham Vocke, genau, der hat das war bei Martin Fowler publiziert. Also wo er irgendwie noch da drauf eingeht, es gibt auch noch Contract Testing und es gibt natürlich noch manuelles Explorative Testing, es gibt Akzeptanztests, es gibt UI Tests im Sinne von UI Elemente, also nicht, dass man irgendwie mit Selenium sowas testet. dass man dass man die deployte Anwendung testet, sondern einfach so UI Elemente testet. Also, aber ich glaube für unsere Diskussion ist ist besser, wenn wir sozusagen bei der Basis Basispyramide erstmal erstmal bleiben. Genau. Unit Tests. Also jetzt ist ja die die große Frage, was ist überhaupt ein Unit Test? Das mag jetzt irgendwie banal sein, ne, aber auch also Martin Fowler schreibt schon darüber, was ist denn eigentlich ein Unit Test und dann gibt’s unterschiedliche Meinungen, dann gibt’s ja auch diesen berühmten Artikel Mocks Aren’t Stubs, auch von Martin Fowler, wo er so unterscheidet zwischen Classical TDD und Mockist TDD. Deswegen vielleicht mal so in die Runde gefragt, was ist denn für euch ein Unit Test? Was testet ein Unit Test alles ab? Fangen wir mal mit Jakob an, hat noch nicht so viel gesagt.

Jakob Oswald Ja, ich glaube tatsächlich ist das eine schon eine schwierige Frage, weil ich glaube, das ist ja die die Abgrenzung, was testet das vielleicht nicht, ne? Also die wenn ich jetzt so an Code Strukturen denke, ist testet halt keine Umsysteme in den meisten Fällen und ich glaube, dann geht schon die die Frage los, testet das eine Klasse oder testet es gegebenenfalls auch quasi ein ein Aggregat von Klassen, ne? Wenn ich mich da jetzt z.B. Richtung Richtung DDD orientiere, also sind sind Tests, die quasi mehrere Klassen, die einander referenzieren, testen noch noch Unit Tests, ist das dann eine Einheit, weil sie quasi nur insgesamt funktionieren oder sag ich mal die Schnittstelle nach außen halt eigentlich in die Funktion definiert, aber ich da drunter halt kleinteilig geschnittene Klassen habe. Na, also das ist ich glaube, da geht schon die Frage los und ich glaube, das Problem ist oder oder den Wunsch, den man ja eigentlich hat, nämlich also so eine Intention ist ja sowas wie, wenn ich Tests habe und Regression testen kann, kann ich sowas machen wie Refactor Merciless, ne? Also ich ich gehe davon aus, ich habe irgendwie Verhalten codiert in meinem Test und wenn ich den Test laufen lasse, dann sagt mir, na ja, das Verhalten, was ich denke, was da drunter liegt, das das wird auch das ist tatsächlich auch so programmiert. Und wenn ich jetzt sag, okay, ich möchte irgendwas ändern, ich möchte ein Feature einbauen oder sowas, dann ändere ich die Implementierung, aber eigentlich nicht das Verhalten und mein Regression Unit Test sagt mir dann, ja, du hast irgendwas eingebaut, aber die Funktionalität, die vorher da war, funktioniert noch genauso und das ist wird, glaube ich, schwierig, wenn man sagt, Unit Tests beschränken sich tatsächlich auf eine Klasse, weil das glaube ich oft so ein bisschen dem widerspricht, wie ich eigentlich Modelle konzipiere, nämlich, dass ich irgendwie so ein so ein Eingang habe, eine API und da drunter aber vielleicht ein komplexes Modell, was eben auch verschiedene aus verschiedenen Klassen besteht.

Sven Johann Ah, okay, okay. Ja, ich hatte ich hatte ursprünglich ja ich gedacht, also wenn du so los, als du losgelegt hast, habe ich schon gedacht, okay, du gehst in diese Richtung mock ich praktisch alle Klassen um mich rum und teste nur die Funktionalität, ja? Oder teste ich praktisch die ganzen Klassen um mich rum noch mit?

Jakob Oswald Die Frage heißt ja, was heißt um mich rum, ne? Da da geht’s ja schon, glaube ich, los, wo wo fängst du deinen Test an und wo hört er eben auf? Also fange ich an irgendwo eine eine Interaktion zu machen, keine Ahnung, ich ich bin jetzt mal um, ich lege irgendwo ein Konto an oder sowas, ne? Da dann gibt es vielleicht einen Haufen von Bedingungen, die Geschäftsregeln, die abgefrühstückt werden müssen, wo halt irgendwie sagt, okay, das darfst du nur tun, wenn bestimmte Vorbedingungen da sind, du hast irgendwie deinen KYC Prozess durchlaufen, dein Account ist verifiziert, du hast nicht schon Endkonten oder was auch immer und erst dann ist sozusagen mein mein Geschäfts Teil, mein mein Teilprozess quasi erfolgreich erledigt. Und jetzt kann ich das natürlich diese einzelnen Komponenten und Prüfungen möglicherweise in einzelne Klassen auslagern, aber eigentlich ist dann halt die Frage, was möchte ich denn testen? Möchte ich quasi die das das Gesamte testen oder möchte ich diese Einzelteile testen? Wenn ich so Unit Test habe, dann also mein meine These ist, dass selten genug die Einzelteile so viel Komplexität bergen, dass ich in denen refactorn kann, dass ein Test sich lohnt, ja? Also ich ich glaube fast in den meisten Fällen lohnen sich Tests auf einer übergeordneten Ebene, wo ich quasi dann drin, also wirklich Implementierung, also Spezifikation und Implementierung soweit auseinander sind, dass ich halt Implementierung ändern kann, ohne dass sie halt Spezifikationen brechen. Und wenn ich halt wirklich diese auf Klassenebene denke, wenn ich jetzt so eine Standard Java Geschäftsanwendung denke, dann haben die einzelne Klassen sind sie oft nur im Zusammenspiel bilden sie halt wirklich so ein so eine logische Einheit ab und im im seltensten Fall eben die einzelne Klasse an sich. Also es gibt sicherlich Ausnahmen, ne, so komplexe Berechnung oder sowas, wenn ich jetzt irgendwie, wenn ich Kryptografie machen würde oder sowas, dann würde ich sagen, ja, okay, das ist total sinnvoll und wichtig halt irgendwie vielleicht nur eine Hashing Funktion zu testen oder irgendeine sehr komplexe Berechnung, aber ich glaube, das ist gar nicht so oft der Fall, sondern oft ist es das Zusammenspiel von eben Klassen oder Objekten, was ich testen möchte, weil das eigentlich die Geschäftsspezifikation ist und die möchte ich testen und wie die nachher abgebildet wird intern, das ist mir ja eigentlich egal, das ist ja auch die die Idee davon zu trennen Spezifikation und Implementierung.

Sven Johann Ja, Daniel, wie deine Antwort?

Daniel Westheide Also ich stimme dazu, dass ein Unit Test nicht zwingend nur eine Klasse testen muss. das sagt aber, glaube ich, auch kaum jemand, das habe ich noch nie gesehen als Definition.

Sven Johann Also da da, also ich ich es gibt ja dieses Paper, also ich glaube, das ist irgendwie London oder European Style TDD, also ich habe vergessen, wie die Leute heißen, aber ich find’s raus, ne? Und das waren halt so ThoughtWorks London Menschen, die haben dieses EasyMock Paper geschrieben und ich bin mir relativ sicher, dass sie sagen, ein Unit Test testet eine Klasse und alles was nicht in der Klasse ist, also also alle Dependencies der Klasse werden gemockt.

Daniel Westheide Mhm.

Sven Johann Also also diese Definition gibt’s auf jeden Fall.

Daniel Westheide Okay, aber dann mache ich mir einfach noch ein paar private Klassen, um das ein bisschen besser zu modularisieren. Also da sieht man ja, eigentlich teste ich irgendetwas, was eben ein vernünftiges Public Interface hat. Und für mich ist dann ein Unit Test idealerweise auch teste ich das eben auf die Schnittstelle hin und nicht darum teste nicht, was da intern alles an Interaktionen passiert unbedingt. das geht eigentlich auch ein bisschen in die Richtung, was Jakob gesagt hat.

Sven Johann Ja. Verdammt, jetzt haben wir hier gar keinen Disput.

Daniel Westheide Für mich unterscheidet sich eher, also der Unit Test zeichnet sich halt auch dadurch aus, dass ich keine externen Abhängigkeiten auf Umsysteme z.B. habe. Datenbanken oder andere Systeme. und dass ich eben die volle Kontrolle auch darüber habe, was ich da teste, wie ich es teste. Und wenn das alles irgendwelche Java Klassen z.B. sind, die zusammenspielen, die aber nicht mit Datenbanken oder anderen Systemen reden, dann kann das doch ein super eine super Einheit sein, die man gut testen kann.

Jakob Oswald Ich glaube an der Stelle kann ich jetzt auch den Disput aufmachen. Ja. Weil ich ich glaube oft genug zumindest in dem Umfeldern, in denen ich mich jetzt so bewegt habe, gehört halt z.B. sowas wie Transaktionalität auf Datenbankebene zur zur Geschäftslogik und da würde ich eben sagen, es ist also da das tatsächlich mein mein Take zu sagen, Unit Tests sind manchmal nicht wirklich aussagekräftig, weil sie eigentlich mittesten müssen, dass die Sachen in der Datenbank konsistent sind. Das heißt, ich kann zwar, ich kann sicherlich Verhalten testen, aber das Verhalten ist eigentlich dadurch definiert, dass auch die Daten in der Datenbank korrekt sind. Also es ist ich ich würde mich nicht trauen sozusagen bei einer Anwendung, die irgendwie größere Summen verwaltet, zu sagen, ja, weil ich die Logik getestet habe, ist das so in Ordnung, weil am Ende die Daten das ist, was relevant sind. Und es muss halt das Zusammenspiel gewrleistet sein, dass quasi mein sowohl meine Logik korrekt ist, als auch wie es dann persistiert wird und dass ich eben auch sowas wie Rollback Verhalten korrekt definiert habe.

Sven Johann Aber wäre wäre das nicht irgendwie so ein Klassiker für ich sag mal so normale Testpyramide, also jetzt egal, ob ich ob ich Classical TDD mache, also Classical TDD bedeutet, ich mocke nicht meine meine Dependencies, ich teste zwar meine Klasse A, die hat Dependencies B, C und D, die werden aber nicht gemockt, da habe ich meine Unit Tests oder ich mache halt, ich mocke alle meine Dependencies. Da, wenn wir bei dem Bankkonto Beispiel bleiben, kann ich natürlich die die also stelle ich mir jetzt so vor, ne? Also ist jetzt bei mir im Projekt auch so, dass sagen, okay, wir haben diverse Validatoren und wir haben Berechnungen und das passiert in einzelnen Klassen. Und diese einzelnen Klassen, die kann man halt isoliert testen, weil ich muss so so eine ich habe zehn Validatoren, die kann ich einzeln testen. Aber ich habe natürlich jetzt so ein Use Case, der halt sagt, okay, dieser Use Case sagt halt Konto anlegen oder Antrag Versicherungsantrag und da will ich natürlich alles im Zusammenspiel testen. Das ist ja eher so ein so ein, wäre für mich so ein Integrationstest, der oder Acceptance Test auf Integrationsebene, der halt sagt, okay, hier gibt’s ein meinetwegen Controller Interface oder ein normales Interface und ich teste alles von oben bis unten inklusive Transaktion. Also für mich für mich schließt es sich so nicht aus.

Daniel Westheide Für mich auch nicht. Also ich habe eigentlich in den meisten Projekten sehr viel Logik, die sich durch Unit Tests sinnvoll testen lässt. Und auf dieser Ebene, wo die Transaktionen stattfinden, sehe ich meistens nicht viel andere Logik. Das ist halt typischerweise so ein Application Service, der dann halt orchestriert ein bisschen, aber eigentlich keine sonst keine Logik enthält. Und den würde ich halt über ein Integration Test dann natürlich auch abtesten.

Jakob Oswald Genau, habe ich jetzt so ein bisschen die Frage, wie aussagekräftig sind dann eben diese einzelnen Unit Tests, ne? Also wie klein sind deine Geschäftsregeln geschnitten und kann ich da drin dann refactorn? Also wenn ich jetzt sage z.B. okay, mein Geschäftsfall braucht jetzt eine neue Business Rule, die ich hinzufüge, die halt irgendwie in einer Liste von Validatoren zukommt oder in irgendeiner Rule Engine oder sowas, dann macht es halt nur Sinn das Ganze zu testen oder dann kann ich nur sicherstellen, dass sie auch tatsächlich verwendet wird. Ich kann natürlich die Einzelteile immer testen, ob die korrekt sind, aber ich glaube oft genug ist halt die Komplexität dieser einzelnen Teile nicht hoch genug oder zumindest ist es dann ist es selten so, dass ich ein Einzelteil refactore, ohne dass ich den Test auch refactorn oder wegschmeißen muss. dann sozusagen mein Regression Test an der Stelle irgendwie habe ich zumindest kein Regression Test. Also ja, ich ich würde oder ich schreibe Unit Tests, um sozusagen Korrektheit einzelner Teile für mich zu gewrleisten, aber es sind halt auch oft Tests, die ich die eigentlich sozusagen nicht als Regression Test zumindest funktionieren.

Sven Johann Also ich kann ich sag mal der der teuerste Bock teuerste Bock, der teuerste Bug, den ich in meinem Leben jemals produziert habe, war bei so einem Billingsystem, hat meinen Kunden fast in die Insolvenz geschleudert, ja. und da war es so, also das war so ein älteres System und da war da war es so, dass die die wir hatten viele Unit Tests, aber diese Unit Tests haben halt irgendwie, dann denke ich hier so an an Daniels Aussage, wenn Unit Test sinnlos erscheinen, dann vielleicht weil die Logik in Services mit vielen Abhängigkeiten steckt. Das war bei uns so gewesen. Es gab halt irgendwelche Klassen, die haben irgendwas gemacht, die konnte man auch Unit testen. Aber eigentlich war das alles sinnlos, weil so viel, also dass die tatsächliche Logik war nur im Zusammenspiel, ne? Und dann habe ich halt, als wir dann gesagt haben, nach diesem Bug, okay, jetzt müssen wir wirklich diesen Code, wir müssen ihn besser machen, damit sowas nie wieder passiert. da haben wir halt ein besseres Design gehabt und auch bessere Unit Tests und jetzt also so so verstehe ich auch deine Aussage, Daniel, oder? Also wenn der wenn der

Daniel Westheide Ja, genau.

Sven Johann Wir haben einfach du sagst TDD führt zu gut testbaren Units, die halt auch in sich Sinn machen.

Daniel Westheide Ja, also das ist meine Erfahrung, wenn ich anfange mit den Tests, dann landet meistens nicht so viel Logik in diesen Services, die man nur zusammen mit externen Systemen dann testen kann.

Jakob Oswald Also ich ich versuche auch kritisch mit mir selber zu sein, vielleicht ist das ja auch das Problem tatsächlich, wenn ich jetzt gerade so ein bisschen weiter drüber nachdenke. Gerade wenn ich jetzt so in die Richtung Domain Driven Design oder sowas gehe, dass ich bisher oft in Codebasen gearbeitet habe, wo sag ich mal die reine Geschäftslogik nicht von der Persistenz getrennt ist, sondern man gesagt hat, Geschäfts Entitäten sind auch Entitäten und das halt irgendwie verwoben ist, ne? Vielleicht ist das tatsächlich, wenn ich so eine reine Trennung von Datenmodell und logischem Modell habe, anders, das kann durchaus sein, wenn ich sage, mein Aggregat, was ich baue, das beinhaltet die komplette Geschäftslogik und am Ende fallen halt irgendwie Datenstrukturen raus, die ich dann wieder persistieren. Mhm. Dann kann das schon sein, dass das vielleicht auch einen anderen Mehrwert hat, ne? In in erster Linie geht’s mir aber auch darum, dass ich sage, ich glaube, ich würde mehr Tests immer, wenn ich jetzt auf so einem Aggregate machen, weil ich nur da drüber eigentlich sicherstellen kann, dass die gekapselten, das gekapselte Verhalten, egal wie es da drin implementiert ist, am Ende wieder gleich ist. Also ich glaube, ich würde selten ein einzelnes Value Object oder sowas testen oder seltener, vielleicht schon, je nachdem wie hoch die Komplexität der Geschäftsregeln da drin ist, aber ich glaube mein Punkt ist eher der, also es gibt zwei Punkte, nämlich einmal genau, wo wo setzt denn das Verhalten an, ne? Also auf welcher Ebene und wie stark gehört halt eben sowas wie Transaktionalität dazu? Also ist tatsächlich die das Persistieren in der Datenbank, also die Modelle, die ich daraus generiere, sind die am Ende auch in der Datenbank valide und legitim? Also typisches Beispiel z.B. ich ich habe sowas wie ich ich möchte Summen sicherstellen, ne? Ich habe irgendwo Krediteinträge und die dürfen einen bestimmten Kreditrahmen nicht überschreiten. Das heißt, ich muss irgendwo auf Transaktionalitätsebene sicherstellen mit einem Optimistic Locking oder nlichen, dass das tatsächlich nicht der Fall ist. Das kann ich nicht in der Logik testen, wo ich halt schreibe, okay, klar, ich kann irgendwie Summe in Memory machen, aber in dem Moment, wo ich sie schreibe, muss ich halt eben Transaktionalität der Datenbank mich darauf verlassen, weil erst an der Stelle überhaupt tatsächlich die Konsistenz gewrleistet werden kann. Das heißt, gerade an der Stelle muss ich das mittesten, um das Gesamtverhalten auf Datenebene und Logikebene sicherzustellen. Ja, kann man sicherlich sagen, Testpyramide, man man macht beides. Das das stimmt, ich glaube, ich sehe aber auch oft, dass eben genau dieser Transaktionalitäts Teil vernachlässigt wird, ne? Also wenn ich irgendwo ich ich finde eigentlich ist es auch so ein Anti Pattern, diese so so Spring Tests mit irgendwie oder Repository Tests zu haben, wo irgendwie über den ganzen Test eine Transaktion aufgemacht wird und wieder zurückgerollt, weil das tatsächlich nicht aussagekräftig ist. Also ich ich muss eigentlich Tests, wenn ich meine Repositories teste oder meine Services teste, mit Transaction Commit machen, um sicherzustellen, dass die Sachen funktionieren. Also ich habe auch schon Tests gesehen, die sozusagen Datenbank Dinge testen und die inserten den Test alle fröhlich und glücklich und machen Rollbacks und in der Produktion fliegt’s mir dann um die Ohren, weil in den Tests nie wirklich ein Transaction Commit gemacht wird, sondern die immer wieder zurückgerollt werden, damit die Datenbank sauber ist. Also ich glaube, das sind so Sachen, die mir dann eher, wo ich gebrannt bin, wo ich denke, ja, es ist super, dass wir ganz viele Tests geschrieben haben und Persistenz Tests, aber in den Transaktions Commit mal dabei war und man da dann eigentlich erst sieht, oh, das funktioniert so gar nicht.

Sven Johann Mhm.

Daniel Westheide Ja, würde ich zustimmen, aber ich glaube, das ist dann halt auf Integration Test Ebene kann ich dann eben mit sehr wenigen Tests diesen wichtigen Teil abdecken. Klingt für mich aber so, wie du das gesagt hast, dass da ganz viel Logik ist, die ich auch anders testen kann, wahrscheinlich über Unit Tests.

Sven Johann Ja, aber das also ich sag mal, die ich erinnere mich an so eine Aussage von ist schon länger her von dem Arbeitskollegen von mir, der meinte ja, Geschäftslogik, Geschäftslogik, bei uns steckt die ganze Geschäftslogik in SQL Statements. Und also da wäre es ja so, da bleibt einem ja nichts anderes übrig, also weil es ein extrem Beispiel. Ja. Aber ich würde auch sagen, dass bei wenn man jetzt irgendwie from Scratch loslegt, also ich selbst selbst wenn man vom Scratch loslegt, kann man immer noch ich sag mal Designfehler haben, aber typischerweise bei existierenden Systemen, wo so Sachen ein bisschen vermischt sind, also Logik und ich sag mal, ich nenne jetzt einfach mal Infrastruktur, dass dass man da natürlich in die Falle läuft, wenn man sagt, ja, wir haben viele einzelne Unit Tests, aber wegen ich sag mal schlechter Struktur nutzt uns das überhaupt nichts und dann braucht man tendenziell mehr mehr Integrationstests.

Daniel Westheide Mhm.

Sven Johann Ich bilde mir sogar, sorry, eine Sache noch, ich bilde mir sogar ein, dass wenn dass wenn wenn wenn wenn Firmen z.B. von von Kobold weg wollen, dass man dann sagt, wir wir gehen sogar ganz weit nach außen, wir machen nur Akzeptanztests gegen Interfaces, weil alles andere bringt natürlich irgendwie nichts, ja, und dann versuchen wir so pö pö das Ganze, also die Systeme auszutauschen.

Daniel Westheide Mhm.

Sven Johann Ja. Da macht ja.

Daniel Westheide Ja.

Sven Johann Sorry, ich habe dich unterbrochen.

Jakob Oswald Aber auch da ganz kurz um da einzuhaken, auch wenn wir über Akzeptanztest sprechen, ich glaube, das ist halt die eine Seite, nämlich die User Facing Seite und die andere Seite ist für mich halt die Datenseite. Das ist genau auch das, worauf ich so ein bisschen abziele, dass der Benutzer sagt, okay, das sieht schön aus, ist und sieht erstmal korrekt aus, ist eine Sache, dass meine Daten dann auch konsistent sind, ist halt die andere und das ist das, was ich denke, was halt wichtig ist.

Sven Johann also das meinte ich z.B..

Jakob Oswald Da wo wir uns bewegen, also zumindest oder wo wo ich mich oft bewege in den Kontexten.

Sven Johann Ja, also ich meinte mit, vielleicht müssen wir noch mal ein bisschen definieren, also mit mit Akzeptanztest meine ich noch nicht mal unbedingt das was der Benutzer sieht auf der UI, sondern für mich so, ich sag mal jetzt Oldschool Uncle Bob oder ein bisschen neuer mit mit Cucumber, da dass ich ein Akzeptanztest schreibe, aber dieser Akzeptanztest, der kennt keine UI, der geht einfach auf der geht auf APIs.

Jakob Oswald Aber wo machst du denn Assertion? Das ist ja meine Frage, ne? Machst du die nur in der Response, krieg ich ein 2 zurück und ist okay oder gucke ich auch, dass in der Datenbank tatsächlich die Daten korrekt geschrieben sind und eben z.B. keine Constraints verletzen?

Sven Johann Ah, so meinst du. Genau, also im Moment ist es so, wir gucken tatsächlich nur auf Outcome.

Jakob Oswald Also Constraints meine ich damit nicht nicht Datenbank Constraints, weil dann würde ich vielleicht kein 2 bekommen, aber Ja. wenn ich z.B. keine Optimistic Locking mache und zwei parallele Transaktionen habe, habe ich plötzlich Kreditsummen, die den Kreditrahmen sprengen und dann kriege ich irgendwann Ärger.

Sven Johann Ja, ah ja, okay, nee, das also da muss ich sagen, ich bin da bin ich bin ich ein bisschen inkonsistent. Also im Moment ist so, wir gucken tatsächlich nur auf Outcome. Also, was ist der Response? Aber ich kenne es auch von so Bestandssystemen im so Lebensversicherung und so Berufsunfigkeit. Da wird, also wenn du so Verträge im Time Travel 40 Jahre fortschreibst, da reicht natürlich nie zu gucken, haben wir jetzt war immer erfolgreich, sondern da wird am Schluss geguckt, also da musst du praktisch in die in die Tabellen reingucken und sagen, ja, da muss jetzt als Auszahlungssumme 723.0 € rauskommen. Aber da hast das machen wir zu selten, glaube ich, ja. Also ich jetzt fühle ich mich ertappt, ne, weil ich denke so, ja, ich gucke eigentlich ziemlich oft einfach nur auf den Response, ja. Aber ich würde auch erwarten, dass wenn es erfolgreich, ne, ich bringe mich jetzt auf die Küche.

Daniel Westheide Aber guckt ihr wirklich in die Datenbank? Also das ist für mich eigentlich ein bisschen auch ein Antipattern, dass ich so Whitebox Integration Tests mache. Das heißt, ich weiß ich nicht, denke ein Repository persistiert was und danach gucke ich, dass es da gucke ich wirklich in die Datenbank rein. Ich würde eher einen Contract machen und sagen, nach Save muss das Ergebnis, wenn ich find by ID mache z.B. auch wieder ausgelesen werden.

Jakob Oswald Ja, für mich sind das schon die die beiden Teile, ne? Also kriege ich einmal die Response von meinem System und hat sich der State verändert, den ich persistiere. Das sind für mich eigentlich die beiden Kerndinge, die ich testen möchte und ist der State der, den ich erwarte. Also vielleicht bin ich da auch ein bisschen fokussiert gerade drauf aus diesem aus meinem letzten Projekt, wo ich quasi Fachlichkeit geschrieben habe, wo es halt genau darum ging zu sagen, du musst halt irgendwie gucken, dass du irgendwelche Summen sicherstellst und das muss halt transaktional sein. Und wenn es halt um Geschäftskredite geht, dann sprechen wir halt über größere Summen und da möchte man dann schon, dass die Ergebnisse korrekt sind und nicht mal hier oder da mal eine Million mehr ausgerechnet wird.

Daniel Westheide Mir ging es nur darum, ob du dabei in den Tests einfach direkt in die Datenbank guckst oder ob du quasi ein E vereinbartes Interface noch mal ansprichst.

Jakob Oswald Nee, die Datenbank.

Daniel Westheide Also ja sagt, dass guckt ihr in die Datenbank?

Jakob Oswald Nee, würde ich nicht machen, also ich würde tatsächlich in die Datenbank gucken mit mit möglichst niedrigschwelligen Mitteln, also JDBC oder sowas, ne? Ich würde kein Repository nehmen. Am Ende hat mir da irgendjemand Mock untergeschoben oder so, dann kommt da halt was zurück. Nein, also kann man sich sicherlich drüber streiten, was dann sozusagen die Schnittstelle ist, mit der man testet, aber tatsächlich würde ich Tests sozusagen auf der Ebene, ich mache ein Service Call, der irgendeine Geschäftsfunktionalität beinhaltet, inklusive Geschäftslogik und Persistenz, gucken, dass die Antwort in Ordnung ist und dass mein Stand in der Datenbank eben unabhängig von Code, ne? Also ich möchte eigentlich nicht den Code, der meine Logik implementiert zum Prüfen nehmen, also bei Repository die generiert sind, kann man sich da sicherlich streiten, aber ich ich würde das quasi unabhängig machen, ne? Ich will nicht den gleichen Code zum den ich teste auch zum zum Prüfen nehmen, weil wer weiß, ob da ein Cash drin ist z.B.. Ne, wenn ich jetzt sage, ich habe irgendwo ein Repository Implementierung, die halt irgendwo ein Right through Cash hat oder sowas und nicht immer committed und ich frag die dann wieder, dann kann es sein, dass er seine Antwort aus dem In Memory Cash holt, aber in die Datenbank persistiert hat eigentlich, weil er irgendwo auf ein Commit wartet oder sowas, was ich aber nie gemacht habe, weil ich meine Transactional vergessen habe oder weil ich zwei Transactional dran habe, aber irgendeine Konfiguration in meinem System schief ist, so dass die Transaktion nie ausgeführt wird und das sind halt die Dinge, wo ich denke, okay, das ist das, was eigentlich schwer zu testen ist. So Code und Logik ist einfach zu testen, aber genau gerade, wenn ich an so Spring Boot Magic Transactional Annotation denke, wird dann überhaupt eine Transaktion aufgemacht, stimmt die Isolation oder ruft dann Service einen anderen Service auf und bräuchte ich eigentlich ein Safe Point und unterstützt der Safe Point, unterstützt die Datenbank mein Safe Point überhaupt? Habe ich eine Datenbank, die das kann oder habe ich eine H2 DB drunter, die immer sagt, ja ja, alles ist gut, aber sobald ich das mal auf eine Oracle oder sowas setze, sehen die Sachen ganz anders aus. Ja, deswegen bin ich da. Also ich bin ja gar kein Verfechter von zu sagen, man sollte keine Unit Tests schreiben. Ich glaube bloß, dass eben das Verhältnis von Unit Tests und Integration Tests durch auch je nach Bedürfnis nicht unbedingt das der Pyramide entspricht und gerade was Regressionsverhalten angeht, ich persönlich mich mit Integration Tests wesentlich wohler fühle oder die Erfahrung gemacht habe, dass wenn ich Unit Tests zu klein schneide oder dann halt sehr beim Design drauf achten muss, wie ich sie schneide, weil ich wenn ich quasi meine Unit anpasse, dann immer den Test auch anpassen muss, dann ja, habe ich zwar sozusagen den das Verhalten wieder getestet, aber für Regressionstest taugt’s halt wenig. Und das ist das, glaube ich, was ich eigentlich in der Produktentwicklung oft haben möchte, Regressionstest, weil klar, entwickle ich neue Sachen, aber das was ich oft genug möchte ich eigentlich sicherstellen, dass bisheriges Verhalten nicht geändert hat oder eben ganz bewusst Sachen anfassen, wo ich will, dass sich Verhalten ändert. Vielleicht bin ich aber auch zu fokussiert auf dieses Regression Testing. Das ja, aber es macht für mich den Mehrwert, ne? Also genau dieses Refactoring Mercy, ich kann einfach wild rumwühlen, Objekte um umbauen, meine Modelle neu definieren, weil ich denke, oh, das Modell passt nicht mehr zu dem, weil ich neue Features habe, aber ich kann halt sicherstellen, dass das was ich möchte, dass wo sich das Verhalten nicht geändert hat, weiterhin auch diesem Verhalten entspricht und ich halt nicht irgendwo was kaputt gemacht habe.

Daniel Westheide Mhm.

Sven Johann Mhm. Ja, also ich wollte auch noch mal kurz auf diese gucke ich in der Datenbank nach. Also bei meinem Bestandssystem, das ist schon lange her, ja, aber da war es auch so, wir gucken tatsächlich in der Datenbank nach, ob diese Zahlen, die da in den, ich sag mal in den Kontotabellen gestanden haben, ob die korrekt sind. Und ja, hat also da ist auch so ein ganzer Rattenschwanz dahinter, weil natürlich irgendwelche Aktuare müssen das alles ausrechnen und so weiter und dann dann wenn die sich schon so eine Mühe machen, dann kann man sagen, okay, dann gucken wir auch in die einzelnen Kontotabellen rein und überprüfen das, ja. Aber das gilt, also da hätte ich jetzt gesagt, okay, für so für so Berechnungen mache ich das, aber ich sag mal, wenn ich einfach so so Datenobjekte hinterherschiebe, also ich ändere irgendwelche Daten, ich habe nicht so besonders viele Berechnungen, dann würde ich es z.B., also da mache ich das nie. Dann verlasse ich mich einfach da drauf, dass ich habe halt ein Repository jetzt mit Spring. Und da gibt’s so ein paar Tests, die gucken, ob ob das funktioniert, aber in meinen Integrationstest okay, ich geb’s zu, da habe ich halt auch eine H2 oder so. Also jetzt nicht mehr, ja, aber weil manchmal nutzen wir auch Datenbank spezifische Dinge und so weiter, da funktioniert das eh nicht. Aber genau, peinlicherweise. Schneide ich raus, schneide ich raus diese Aussage, habe ich nie gesagt.

Jakob Oswald ist glaube ich ja auch stark davon abhängig, wo man sich gerade bewegt, ne? Also ich meine, es ist ja auch nicht immer so, dass man dass man jetzt darüber redet, dass man irgendwie Millionen Kredite vergibt, sondern manchmal sind es ja auch irgendwie andere Sachen, wo ich sag, okay, dann habe ich da halt E-Commerce irgendwie ein paar Artikel, habe ich habe ich jetzt manche Leute würden es vielleicht anders sehen, aber das ist jetzt vielleicht nicht nicht geschäftskritisch, wenn ich mal einen Artikel zu viel versende oder sowas. Na, also das wird jetzt in den meisten Fällen nicht mein mein Geschäft ruinieren. Das ist natürlich nicht schön und ist vielleicht nicht optimal, aber könnte ich jetzt mit leben, ja? Also ist jetzt vielleicht auch man muss ja vielleicht auch nicht mal alles testen, das ist glaube ich der Punkt, ne? Oder auf einer bestimmten Ebene, sondern ich glaube, es kommt natürlich immer drauf an, ob wo man sich bewegt, was man in welcher Detailtiefe testen möchte oder ob man halt irgendwann sagt, okay, vielleicht ist es jetzt auch sag ich mal nicht mal wirtschaftlich, ja, weil ich keine 1% Testabdeckung habe auf allen Ebenen.

Sven Johann Ja, also Risikobasiertes Testen, ne? Also Sachen, wo wo es wahrscheinlich ist, dass es dass was kaputt geht und wenn es kaputt geht, uns wirklich in den, also das halt sehr schlimme Auswirkungen hat, da muss man natürlich alle Register ziehen, ja? Und weiß ich halt bei der Adressverwaltung der Professorenliste, das ist vielleicht nicht so wichtig, ne? Also ja. Damit will ich jetzt sagen, man sollte nichts testen, habe ich beim Kunden auch gerade die Diskussion, die Leute sagen, ja, wir wollen ja hohe Qualität, sage ich, ja, wir wollen immer hohe Qualität, aber gibt’s halt Opportunitätskosten, also so, man kann halt, vielleicht gibt’s sinnvollere Dinge zu tun, als Krimskrams bis zum Abwinken automatisch zu testen auf allen Ebenen, ne?

Jakob Oswald Genau, also der was ja auch eingangs gesagt hast, Motivation so so schnelles Delivery, ja? Also man muss halt natürlich also Integrationstests oder Systemtests zu schreiben kommt halt auch mit Kosten daher, ja? Und das sind genau diese Kosten wie Laufzeit, also Testlaufzeit, also und damit halt quasi Iteration und da muss man auch irgendwann anfangen sich Gedanken zu machen, wenn man so eine Teststrategie verfolgt, wie schneide ich das eigentlich, ne? Also habe ich irgendwie nur sowas wie Smoke Tests oder habe ich halt Testsuiten, die dann halt über Nacht laufen oder so, weil wenn die nachher zwei Stunden laufen, dann will ich auch nicht bei jedem Commit warten, bis ich den deployed habe, sonst komme ich da auch irgendwann in Teufels Küche. Und im schlimmsten Fall, wenn es jetzt auch noch jedes Team so macht, dann muss man ja auch gucken, vielleicht bringt meine Entwicklungsinfrastruktur das überhaupt her, dann muss ich vielleicht auch schauen, haben meine CI Server, sind die entsprechend geschnitten, dass ich für meine System und Integrationstests vielleicht eigene Kisten habe oder sowas, damit sie nicht die Standard Builds oder Deployments halt irgendwie in den Abgrund reißen. Also es ist nicht ohne Tücken zu sagen, ich mache jetzt also nur das eine oder ich mache jetzt überwiegend das eine. Also es hat auch eklatante Nachteile, ne? Oder Flakeyness. Wenn ich jetzt irgendwie jeden Build, vor allen Dingen, wenn er zwei Stunden dauert, dreimal anstoßen muss, dann bin ich jetzt schon bei sechs Stunden, so irgendwie auch nicht so cool. Also damit habe ich dann meine schnelle Iterationszeit auch quasi eliminiert. Da muss man glaube ich, es ist auch da wieder eine Abwägungssache und das ist halt auch die Frage, sind das Teams, die tatsächlich dreimal am Tag produktiv deployen oder habe ich eben doch Monats Releases, weil da ich in regulierten Umgebung unterwegs bin, wo halt zum Release noch elementarer Dokumentationsaufwand gehört, der nicht voll automatisiert ist, wo meine Kunden sensibel für Änderungen sind oder nliches, ne? Da ist es dann vielleicht auch nicht so schlimm, wenn meine Regressionsänderungen über Nacht getestet werden und ich dann morgens gucke, habe ich gestern was kaputt gemacht, aber es ist es ist mit Sicherheit ein zweischneidiges Schwert, ja? Es ist nicht gibt auch da nicht den Silver Bullet natürlich.

Sven Johann Ja, ich wollte nur einmal ganz kurz auf eine Aussage zurück von Daniel. Also wir hatten jetzt irgendwie so gesagt, ideal ist es nicht, wenn wenn meinetwegen Geschäftslogik und transaktionale Logik vermischt sind, happens, ja? Also die die Frage ist halt nur, also Daniel hat mal gesagt, TDD führt zu gut testbaren Units. Also wenn wir jetzt auf der auf der grünen Wiese anfangen und wir machen alles richtig, ja, wie wie würde die Welt dann deiner Meinung nach aussehen?

Daniel Westheide Die Welt?

Sven Johann Ja, die die also die die Testing Welt.

Daniel Westheide Ja, wenn wir konsequent TDD machen, dann würde ich halt auch schon erwarten, dass wir eigentlich sehr nah an der Testpyramide dran sein können, ja? Weil ich halt eigentlich auch zu einem Design komme, wo ich die Logik eben wirklich unabhängig testen kann. Ich würde da allerdings tatsächlich was die Mocks angeht, ein bisschen vorsichtig sein. Was ich halt oft erlebt habe, ist, wenn wenn ich halt ein Unit Test schreiben möchte, dann wird halt werden 10 Mocks verwendet für alle Abhängigkeiten. Ist schon ein schlechtes Zeichen, dass ich so viele Abhängigkeiten doch habe in meiner Unit. Und mit diesen Mocking Frameworks mache ich dann ja so ein Ad hoc Mock oder Stub.

Sven Johann Mock and Stub.

Daniel Westheide Ja, du kannst ja beides damit machen mit diesen Frameworks.

Sven Johann Ja, ja.

Daniel Westheide Aber die egal, ob das Mocks oder Stubs sind, die das ist ja überhaupt nicht sichergestellt, dass die den Contract einhalten, der für diese externe Abhängigkeit eigentlich gilt, ne? Weil ich eben Ad hoc irgendwie beliebig die programmieren kann und den sagen kann, wie sie sich verhalten sollen. Und das sehe ich eigentlich als Problem, wenn ich exzessiv mit diesen Mocking Frameworks arbeite.

Sven Johann Ja.

Daniel Westheide Und ich würde halt lieber zu einem Design kommen, wo ich gar nicht dann so viel Mocken muss, ne? Also viele Sachen lassen sich ja auch anders lösen, indem ich z.B. anstatt mir sagen wir mal, ich möchte noch eine E-Mail verschicken in meinem Application Service, nachdem ich irgendwie ein Aggregat gespeichert habe, dann kann ich ja in Spring z.B. mir so ein Event Publisher injizieren lassen, den müsste ich dann vielleicht Mocken, um das irgendwie zu testen, dass das passiert, oder ich gebe halt einen Application Event zurück, ne? Und habe dann irgendwo einen Event Listener, der auf diesen Event hört, dann habe ich das ja auch schon mehr entkoppelt. Und dann kann ich super in einem Unit Test sagen, wird denn dieser Event zurückgegeben oder nicht? Als kleines Beispiel.

Sven Johann Ja. Wenn es so funktioniert, ja?

Daniel Westheide Ja gut, man verlässt sich dann natürlich auf die Infrastruktur in diesem Fall von von Spring, dass dann da entsprechend auch der Event Listener aufgerufen wird.

Jakob Oswald Ja.

Sven Johann Ich frage mich, ich frage mich gerade ob das ob die Diskussion, ob man ob Unit, ob man mehr Unit Tests, also ob man Testpyramide oder ich sag mal Testdiamant haben will, ob da nicht vielleicht sogar Domänen abhängig ist, ja? Weil Jakob sagt halt, na ja, er war halt bei einer Bank und ich kann mich erinnern, den den Artikel suche ich auf jeden Fall noch mal raus, da gab’s, es gab ja mal so ein Disput zwischen Uncle Bob und James Coplien zu Unit Tests und Integrationstests und da hat James Coplien hat halt so argumentiert, ich hoffe, ich kriege das richtig zusammen, aber James Coplien hat so argumentiert, es gibt halt Situationen, da kannst du keine, da da da spiegelt praktisch so ein so ein Unit Test spiegelt halt null die wirkliche Realität von Interaktion von Objekten wieder. Und dann kommt er genau mit so einem Konto Beispiel dazu erklären und sagt halt in diesem ja, also du kannst vielleicht mal so ein paar Unit Tests schreiben, aber dann musst du ein Integrationstest schreiben, um weil also der Unit Test, der hilft dir vielleicht so früher Fehler zu finden, aber um wirklich sicher zu sein, das funktioniert, brauchst du einfach diesen Integrationstest und wenn du diesen Integrationstest hast, dann kannst du die Unit Tests löschen. Also das war sehr kontrovers, weil er halt irgendwie argumentiert hat, dass man sogar Tests, die man mal geschrieben hat, wegwirft. Und ich bilde mir ein, ist sogar die korrigier mich Jakob, aber hat glaube ich auch deine Argumentation gehabt, dass die Unit Tests, also du änderst Code und der Integrationstest bleibt grün, also machst ein Effekt, der Integrationstest bleibt grün, aber du musst halt trotzdem einen Haufen Unit Tests noch mal anpassen, weil die halt zu eng gekoppelt sind, ja? Und dann sagt er halt, das ist viel zu viel Arbeit, weg damit, ja?

Jakob Oswald Ja, genau, im schlimmsten Fall, ne? Also wenn ich jetzt für alles ein Unit Test geschrieben habe, obwohl es vielleicht eben keine komplexe Logik beinhaltet, dann muss ich halt im schlimmsten Fall den eben refactorn und bringt mir halt schlichtweg keinen Mehrwert. Aber ich glaube, es gibt schon, also vielleicht auch mal sagen, also es gibt, wenn ich Tests aufbauen würde, würde ich z.B. immer sagen, Unit Tests mache ich mehr Permutation, ne? Also sozusagen das das Teil die Teile meiner Unit kann ich dann in verschiedenen Edge Cases oder Varianten testen, das würde ich halt irgendwann nicht mehr machen, also wenn ich ein Integrationstest schreibe, würde ich vielleicht nicht mehr so viele Edge Cases testen oder zumindest nicht in der Varianz, wie halt in dem Unit Test. Also ich glaube, da gibt es macht es schon Sinn das zu differenzieren oder halt zu sagen, wofür lohnen sich denn Tests oder ich habe halt, ich teste eigentlich verschiedene Dinge vielleicht auch, ne? Das eine ist halt wirklich Input Validierung, Edge Cases etc. und das andere ist dann vielleicht eher auf der Ebene, wenn ich vernünftigen Input habe, werden dann auch die Daten vernünftig persistiert. Ja, und auch an der Stelle mit dem vernünftig gibt’s glaube ich auch noch mal eine Sache, was was z.B. schwer schwer zu provozieren ist, sind ja manchmal z.B. Fehlerverhalten von Umsystem, ne? An der Stelle kann ich eigentlich fast nur noch auf Mocks zurückgreifen, also wenn ich jetzt eben nicht nur Happy Pass Test schreibe, sondern mal sag, reagiert denn mein System sinnvoll auf Fehler? Wie geht es denn eigentlich damit um, dann muss ich ja eigentlich irgendwann anfangen, mich irgendwo vielleicht in Repository einzuhaken und mal irgendwie eine Exception zu werfen oder sowas, weil ich es vielleicht nicht so einfach hinkriege, dass meine Datenbank an der Stelle sagt, oh, ich bin jetzt nicht da oder sowas. Also vielleicht kann ich dann irgendwie schon bestimmte Fehler provozieren, aber manche Fehler sind vielleicht eben schwer zu zu provozieren und das kann ich durch Mocken natürlich wunderbar machen, nämlich mal ein Fehlverhalten in einer Dependency quasi simulieren und schauen, funktioniert denn der Rest dann noch, ja?

Daniel Westheide Ja, da stimme ich zu. Oder gut, ja, mit WireMock oder so könnte ich das machen, auch wenn ich jetzt eine externe HTTP API anspreche, aber es ist halt letztlich auch ein Mock, wie der Name sagt.

Jakob Oswald Ja.

Daniel Westheide Genau. Ansonsten, ja, ich habe auch schon Codebasen gesehen, wo tatsächlich in Unit Tests und dann noch mal in Integration Tests und noch mal in anderen Integration Tests auf einer höheren Ebene immer wieder das gleiche in allen Permutationen getestet wurde. Das habt ihr ja auch gesagt, das ist halt nicht sinnvoll. Da muss ich es halt irgendwie an zwei, drei Stellen immer wieder alles anpassen, wenn wenn sich die Logik ändern soll.

Sven Johann Ja, ich ich leide, ich leide gerade. Also bei bei uns bei uns ist tatsächlich so, dass also bei dem jetzigen Kunden ist jetzt gekippt, ja, also letzte Woche Sektflaschen, Sektkorken sind rausgeflogen und so. Aber da hatten wir sozusagen den Test, also ich habe den Test Quadrant genannt, weil auf allen Ebenen, auf Unit Test Ebene, also wir haben also okay, noch mal von vorne, auf auf Unit Test Ebene, auf Integration Test Ebene und auf UI Test Ebene wurde praktisch dreifach alles getestet. Ja, also du hast irgendeinen Edge Case in irgendeinem Validator, den testest du mit dem Unit Test. Den testest du mit einem Integration Test und du hast einen Selenium Test, der praktisch auf der bis zum 27. Reiter vorgeht und dann diesen Fehler provoziert, diesen Edge Case, wo sie und dann und dann laufen halt Tests halt auch einfach 24 Stunden, ja, also ist völliger Wahnsinn, ja. Und da hat aber eine Weile gedauert, um die um die Leute abzuholen, also weil der ist halt auch Finanzen und so weiter, um zu sagen, hey, ihr habt kein ihr habt keinen Nachteil. In also der Fachbereich war so ein bisschen besorgt, ne, dass sie halt sagen, dreifach hält besser und kann sagen, ja, das stimmt, aber verursacht halt riesige Kosten und eigentlich testen wir auf unterschiedlichen Ebenen unterschiedliche Dinge. Beim Automobil ist ja auch nicht so, dass wir einzelne Schrauben testen, wenn wir Testfahrten machen, sondern die einzelnen Schrauben, die sind halt auf Unit Test Ebene beim Schraubenhersteller. Da wird geguckt, ist das Material in Ordnung, ist hat gepasst hat Gewinde, ist das gut gefräst, wie auch immer, ja, also. Also ja, wir wir leiden da.

Jakob Oswald Man merkt schon, du bist leid geplagt, ja.

Sven Johann Ja.

Jakob Oswald Und an der Stelle Apropos leid geplagt, ne, also man muss halt dazu sagen, es gibt halt in regulierten Umgebungen auch die Anforderung zu sagen, alles, was du quasi fachlich spezifizierst, muss auch getestet sein. Das ist jetzt frag mich, welche aus welcher Vorgabe das stammt, aber es ist irgendeine Waffenregulierung, meine ich. Und an der Stelle ist natürlich genau die Frage, wie wie bildest du denn nachher z.B. diesen Dokumentationsnachweis ab, ne? Also habe ich jetzt eben automatisierte Acceptance Tests mit Selenium, wo ich dann sage, okay, da fällt dann hinten irgendein Report raus und den hänge ich dann an. Aber da kann ich dann eben, wenn ich dann eben Verhalten spezifiziere, wo ich sage, okay, das will ich eigentlich gar nicht auf UI Ebene testen, dann bleibt am Ende halt irgendwie so ein JUnit Report oder so. Ist dann die Frage, wie da hängt hängt man das dann wieder mit an und also es ist sag ich mal die die Dokumentationsanforderung, die musst du dann halt auch irgendwo wieder mit abbilden können, wo du sagst, diese dieser User Story, wo ich sage, wenn ich so und so viel Kredit vergebe, dann muss sichergestellt sein, dass der Rahmen, der beschlossen wurde, nicht überschritten wird. Das muss ich halt irgendwo nachweisen, dass ich das getestet habe. Vielleicht oder hoffentlich habe ich das auch getan, möglicherweise halt in einem Integration Test in irgendeinem JUnit Code, weil ich es halt eben denke, okay, es ist wichtig, also ich muss die Datenbank mit einbeziehen, aber ich will es jetzt vielleicht nicht auf UI Ebene machen, weil noch Flakier und dort noch länger und die Permutation, die ich da durchgehen will, ist mir eigentlich zu hoch für diese Test Ebene, aber da muss ich halt trotzdem irgendwie idealerweise auch irgendwie automatisiert noch generieren, dass dieser Test halt diese Anforderung abdeckt. Und das ist, glaube ich, auch so ein Teil, ne? Also irgendwie auf Selenium Ebene lassen sich eher z.B. Xray Reports JSON generieren. Ich glaube für JUnit habe ich das bisher nur Hand gestrickt gesehen, dass Leute Annotationen an Tests hängen und sagen, okay, irgendwie kann ich daraus dann spezifizieren oder generieren, dass das halt einen eine User Story eigentlich abdeckt. Ja, also die die Dokumentationspflicht, die da dran hängt, ist, glaube ich, auch manchmal etwas, warum man in regulierten Umgebungen dazu tendiert, Tests auf höherer Ebene zu machen, weil das leichter ist, das zu dokumentieren.

Sven Johann Ja, also wie bei uns, wir sind ja auch eine wir wir sind ja auch eine regulierte Umgebung und die Integration Tests sind auch Cucumber Tests, nur dass die Cucumber Tests kein Selenium gegen die UI, also also die gibt’s auch. Aber da ist einfach der der Klucode geht gegen den gegen den Controller, ne? Und das ist sozusagen unsere unser Nachweis, dass wir praktisch alles was, also man muss auch dazu sagen, unsere UI, also ist zwar eine SPA, aber eigentlich auch wieder, also sieht SPA Technologie aus, ist es aber nicht, aber praktisch bei uns sind die Abnahmetests gegen die also die einzelnen Stories werden gegen die API abgenommen und dann gibt’s natürlich noch so, ich sag mal User Journey, wenige User Journey Tests, die als von den ganzen Prozess abfackeln, aber die kannst du halt dann nicht mehr so einfach an einzelne Stories hängen. Also das funktioniert natürlich nicht, ja. Das sehe ich ein, ja, also jetzt bei uns, okay, mit Cucumber. Ja.

Jakob Oswald Also ich sag gar nicht, dass es gut ist, ne? Ich glaube, das ist eine Herausforderung, dass sie sagen, eigentlich möchte ich jede Test Ebene auch irgendwie dokumentiert und assoziiert haben, weil dann erlaubt es mir auch zu sagen, oh, ich baue jetzt nicht den Enden den Test dreimal nach, sondern ich teste ihn an der Stelle, wo es mir als sinnvoller scheint, nämlich vielleicht sogar als Unit Test, Eingabe Validierung, aber dann muss ich brauche ich halt irgendeine Möglichkeit, mein Test oder sag ich mal eine Testdokumentation zu generieren, wo steht, die Anforderung, dass ich hier nur positive Zahlen eingeben kann, ist halt in diesem Unit Test getestet worden.

Sven Johann Ja, ich also ich also meinst du mit einem, da sind wir Diskussion, was ein Unit Test ist. Also das kann ich mir jetzt z.B. Ja, das kann ich mir jetzt wenig vorstellen, also dass ich in so einer regulierten Umgebung ein Unit Test zeigen kann, der sagt, hier habe ich eine hier sind drei Unit Tests, die haben die Story getestet.

Daniel Westheide Ja, direkt können sie das wahrscheinlich nicht testen, ne? Aber wenn ich halt viel mit mit starker Typisierung arbeite, kann ich natürlich ein Value Object machen, was am am Rand an meiner Systems halt direkt irgendwie die Zahl passt und sicherstellt, dass die immer positiv ist. Und dann habe ich halt ein Unit Test, wo ich zeige, dass das von diesem Value Object umgesetzt wird, diese Logik, aber damit beweise ich ja nicht, dass ich dieses Value Object überall auch benutze in den User Stories.

Jakob Oswald Ja, beweisen ist ja, glaube ich, eh.

Daniel Westheide Ja gut, aber ich habe auch keine, ich kann es auch, ich habe keine Dokumentation dafür jedenfalls.

Jakob Oswald Ja.

Daniel Westheide Selbst wenn ich eine Dokumentation von diesem Test habe, ist halt nicht unbedingt ersichtlich, dass dass eben dieses Value Object dann auch überall eingesetzt wird, wo es eingesetzt werden sollte.

Jakob Oswald Am Ende des Tages ist das ja auch wieder aber eine Balance zwischen, glaube ich, was ist gut genug für eine Prüfung und was nicht. Ja und wahrscheinlich müsste man es halt eigentlich eben auf auf anderen Ebenen testen, aber das ist dann halt, glaube ich, auch wieder die Wirtschaftlichkeitsfrage und die Risikofrage, ne? Also wo teste ich jetzt wirklich auf allen Ebenen alles dreimal oder was ist denn die Definitionsgrenze von so einem Test? Ist das dann sozusagen der Service Layer mit der Transaktionalität? Ja. Wo ich sage, egal von wo es kommt, das speichert meine Sachen sicher. Dann muss ich halt aber auch eigentlich theoretisch nachweisen, dass kein anderes System an meine Datenbank geht. Na, das ist halt auch mache ich das sozusagen per Definition, dass ich sage, hey, per Definition ist es so, dass dieses Objekt in allen Rändern genutzt wird und per Definition ist es so, dass meine Datenbank nur von meiner Anwendung manipuliert wird. Wenn ich mich dann daran halte, dann funktioniert es, wenn ich mich nicht daran halte, dann bringt dir auch sozusagen der Test deiner Anwendungslogik nicht, wenn jetzt irgendeine Anwendung einfach in die Datenbank reinschreibt. Also das beweisen ist sozusagen eh schwierig an der Stelle, deswegen ist, glaube ich, da die Frage vielleicht muss man es dann auch gar nicht technisch abdecken, sondern halt nur per sag ich mal ja, Regel oder etwas, was ich halt irgendwo auf der Meta Ebene beschrieben habe und da muss man sich dann halt dran halten oder sollte man sich dran halten, ne?

Daniel Westheide Ja, mir ist auch gerade noch eingefallen, dass ich auch mal in so einem stark regulierten Projekt war, das war auch eine Bank. Und das bringt mir nämlich zu der Frage, ob wir überhaupt, also die hatten nämlich eine eigene Testabteilung organisatorisch. Das heißt, die Dokumentationspflicht war eigentlich gar nicht im Entwicklungsteam für diese ganzen Tests, sondern die hatten da eh ihre eigene Abteilung für, die dann wahrscheinlich auf UI oder API Ebene getestet hat. ist hat natürlich Nachteile, ne, dass man das so voneinander trennt, aber in so einem Fall müsste das Entwicklungsteam sich ja gar nicht mit Fragen der Dokumentationspflicht beschäftigen, sondern könnte einfach sinnvolle Tests schreiben.

Sven Johann Ja, aber die also bei uns gibt’s gibt’s auch also jede Story wird praktisch manuell natürlich abgenommen. Mhm. aber was ist mit Regression, ja? Also du willst ja jetzt nicht irgendwie noch mal den ganzen Altkram durchnudeln. Und da brauchst du die Nachweispflicht. Also jetzt, ich habe da noch nicht so 1% drüber nachgedacht, weil wir diese weil wir halt praktisch auf auf Akzeptanz Ebene Integrationstest machen. Aber dieses Damoklesschwert Wirtschaftsprüfer oder Barfin hängt halt immer über dir. Und das habe ich z.B. nie verstanden. Ich habe jetzt mit einem Wirtschaftsprüfer gesprochen, ich habe mit mehreren Leuten gesprochen, die permanent geprüft werden, um einfach mal zu verstehen, was Sache ist, ja? Und und da kam im Grunde genommen raus, du musst du musst gewissermaßen schlüssig erklären können, was du machst, ja? Also weil z.B. im Handel, das ist jetzt kein Bank, also mit dem einen Wirtschaftsprüfer war das so, der hat halt gesagt, die können gar nicht, die können natürlich im Detail gar nicht nachweisen, ob ich sag mal die Artikel, die in irgendeinem Geschäft verkauft werden, dass die dass das tatsächlich alle Buchungsgänge und so weiter und Lagerhaltung. Ich meine, das kann ja keiner über sind ja gigantische sind sind viele Systeme, die da zusammenstehen. Im Detail können die das natürlich nie verstehen, aber du brauchst du musst halt schlüssig erklären können, wie das alles funktioniert und so könnte ich mir auch vorstellen, dass das halt, wenn man jetzt sagt, ich habe jetzt keine Akzeptanztest auf API Level und ich will gewisse Dinge halt auf Unit Test Ebene haben, dass man das auch gut erklären kann, weil also wenn man das gut erklärt, dass die Leute halt sagen, ist in Ordnung, ja? Weil weil ins Detail guckt da keiner. Also alle Leute, die ich mit denen ich gesprochen habe, die die geprüft wurden, da wird halt einfach nur gefragt, also die die wollen halt wissen, wie es funktioniert. Kommt man seiner Sorgfaltspflicht nach, ja? Und ob du jetzt so oder so testest, da wurde da niemand gefragt, ja?

Jakob Oswald Die die fragen dann eher nach dem Prozess, ne? Wo sie sagen so, was ist denn euer Prozess fürs Testen? Und wenn man denen dann halt was erzlt, dann kann es glaube ich schon mal sein, dass sie sagen, okay, dann zeig mir das doch mal. Also wenn du sagst, ja, wir machen das so und so und dann dokumentieren wir das da, sagen, ja, dann zeig mal die Dokumentation und dann zeigst du die und dann sagen sie, okay, das was du sagst stimmt. Die gucken sich aber nicht die also vielleicht auch in Teilen die Dokumentation an, aber natürlich nicht in der Tiefe vielleicht, sondern die wahrscheinlich ist es eher das, okay, du hast einen definierten Prozess, du hältst dich an diesen Prozess und wenn der Prozess sag ich mal gut aussieht, dann dann ist das in Ordnung.

Sven Johann Ja, und und zeig mal. Also das passiert natürlich schon, dass man halt sagt, kannst du mir mal sagen, kannst kannst du mir mal eine eine eine Anforderung nennen, die ihr vor einem halben Jahr live gebracht habt, eine größere? Ja, die und die. Okay, dann will ich mal sehen, also geh mal an diesem Beispiel durch, wie das überhaupt in Produktion gelandet ist. Und da kann man natürlich irgendwie fummeln und sagen, hier habe ich IDs, die sind im Repository und wie auch immer, ja? aber wenn es halt nicht nachvollziehbar ist, dann hat man dann gibt’s Ärger, ja? Na ja, wir sind ein bisschen abgedriftet. Genau, haben wir ein Fazit? also machen wir, ich ich probiere mich mal an einem Fazit und ihr korrigiert mich. Also wir sind gestartet, sagen wir jetzt ein Test, also was überhaupt eine Testpyramide? Haben noch mal die einzelnen Testarten kurz definiert und dann war die Diskussion, kann man überhaupt mit Unit Tests alles abtesten, was wichtig ist, ja? Oder alles ist natürlich falsch, ne? Also können wir einen Großteil abtesten oder brauchen wir eigentlich mehr Integrationstests? Haben wir gesagt, na ja, im idealen Fall kommt immer auf die Domäne an, aber in den meisten Domänen ist so, wenn man es korrekt macht, kann man der Testpyramide folgen. Man hat sehr viele Unit Tests, weniger Integrationstests. Es gibt allerdings auch Code, da ist Logik und ich sag mal Transaktionen Orchestrierung von die von Umsystem ist halt dabei. Da wird’s halt schwieriger, da hat man keine andere Wahl als mehr Integrationstests zu schreiben und wir vermuten, dass auch einfach Domänen gibt, wo es einfach sehr schwierig ist auf Unit Test Ebene was nicht was zu testen, aber dass man halt das Domänen gibt, wo man tendenziell mehr Integrationstest als Unit Tests braucht. Und dann noch abgedriftet in regulierte Umgebung.

Daniel Westheide Und der Unit sollte nicht zu klein sein, ist halt auch ein wichtiger Punkt. Also ich sehe das auch wie Jakob, wenn wir jetzt Logik haben, würde ich schon auch eher so das im DDD Speak jetzt auf Aggregate Root Ebene auf jeden Fall die meisten Tests sehen für diese Logik und nicht zu sehr in die kleinen Teile des ganzen Aggregates reingehen und Einzeltest zu machen.

Sven Johann Okay. Ja. Auch nicht von Anfang an sozusagen. Also so dass wir dass wir mit Unit Test beginnt und dann die irgendwann wegwirft. Also den Artikel werde ich auf jeden Fall, den muss ich raussuchen und von von James Coplien. Gut. Ich könnte jetzt sagen, wir wären durch. Gibt hier noch so ein paar Fragen, aber zu Mock, Stubs, Fake, Dummies und so weiter, aber ich glaube über den Punkt sind wir hinaus. ja, dann bedanke ich mich auf jeden Fall. Auch bei den Zuschauern, die es Zuhörern, Zuschauer vielleicht auch, weil wir ja Video haben, für alle, die es bis hierhin geschafft haben. Noch Final Words von euch?

Jakob Oswald Ich ja, ich habe gerade noch mal nachgedacht. Ich glaube mein Final Word wäre, auch wenn man ganz viel Tests schreibt, finde ich das immer extrem sinnvoll, wenn man als Entwicklerin oder Entwickler die Anwendung selber mal ausprobiert. Weil ich glaube, dann sieht man auch erst, ob die Sachen funktionieren. Also so Eat your own Dog Food, sei dein eigener Testerin oder Tester auch noch mal vom Großen und Ganzen nicht nicht nur auf Testautomatisierung.

Daniel Westheide Ja, wir haben das Stichwort Exploratory Testing nämlich eigentlich auch noch in unseren Notizen. Das wäre das so ein bisschen.

Sven Johann Genau, also wir haben uns jetzt auf diese zwei Ebenen konzentriert. Wir haben so nebenbei haben wir noch jetzt nicht explizit über über Contract Testing gesprochen, ne, von von unseren Nachbarsystem, aber ja, genau, also exploratives Testen, manuelles Testen über die UI,

Daniel Westheide Ja, sollte man tun, muss man glaube ich auch lernen, da so ein destruktives Mindset zu haben. Ich meine, allerersten Firma hatte ich einen externen Tester, der da sehr gut drin war. Der war sehr kreativ und hat lustige Fehler gefunden. Und ich glaube, das würde uns allen gut tun als Entwicklis, wenn wir da auch das hin und wieder mehr machen.

Sven Johann Also ich will jetzt kein ganz neues Fass aufmachen, aber irgendwie muss ich, weil ich ich weiß nicht, ob wir das können. Ob es nicht also so manuell testen so gut wie es geht, okay, aber hat man diese destruktive Denkweise? Also weil ich mir gut vorstellen kann, das habe ich einmal erlebt, das hat so halb gut funktioniert, war, dass ich mit einem Tester meine ganzen, also dass so ein Tester halt schon mal so genannt hat, so typische Dinge auf auf die du niemals kommen wirst, wenn du diesen Use Case nachher manuell testest, ja?

Jakob Oswald Klingt ziemlich die.

Sven Johann Ich hatte die zehn Dinge, die du auf die du niemals kommen wirst.

Jakob Oswald Ja, genau.

Sven Johann Ja, ja, hat er stimmt aber, stimmt aber.

Daniel Westheide Aber was du wahrscheinlich ja. Du hast natürlich einen Punkt.

Sven Johann Nee, sag’s, sorry.

Daniel Westheide Ja, weil du weil du es selbst entwickelst, kommst du vielleicht sowieso nicht da drauf. du hast nicht diese Außenperspektive. Das ist glaube ich auch ein wichtiger Punkt, dass jemand der wirklich einfach nur ein User quasi ist und das nicht so nicht so vertraut mit den Interner ist, der kommt auf ganz andere Ideen. Wahrscheinlich.

Jakob Oswald Aber zumindest den Happy Pass kann man ja selber mal.

Sven Johann Ja, ja.

Daniel Westheide Ja, das auf jeden Fall.

Jakob Oswald geht’s ja eher darum, man programmiert irgendwas und dann die Tests sind grün, dann dann kann ich das jetzt über den Zaun werfen oder probiert man selber mal aus und guckt, funktioniert es dann wirklich auch im im Produkt, sofern ich dazu Zugriff drauf habe, aber in den meisten Fällen sollte ich ja irgendwie solche nicht Code, sondern irgendwie Produkte oder sowas und dann halt einmal selber einfach ausprobieren, tun denn die Dinge so, wie ich sie mir vorstelle. Also der eigene Acceptance Tester zumindest für den Happy Pass sein. Das wäre mein Appell an die Teams, ja.

Daniel Westheide Für Fehler auf die man nicht gekommen wäre, würde ich ansonsten das aber dann auch eher technisch kann ich immer nur Property Based Testing empfehlen. Das provoziert auch manchmal Edge Cases an die man auch nicht gedacht hat. Weil es halt zufällige Input Daten generiert.

Sven Johann Hatten wir mal, gibt’s ein INNOQ Podcast mit Jan Stepin dazu?

Daniel Westheide Das weiß ich gerade nicht, aber wir sollten ihn ansonsten ja. Einladen.

Sven Johann Genau, nee, das genau. Ich schreibe mir das gerade mal auf. Gut. Genau, also auf jeden Fall Happy Testing, viel Spaß mit Regulierungsbehörden an alle. Genau. Ich sag das so, aber eigentlich ist ja, also nee, lassen wir das, ja? Aber ich finde ja, da steht da steht eigentlich nie Quatsch drin. Da steht halt drin, testet eure Anwendung, das muss nachvollziehbar sein, macht Backups und so weiter. Da steht eigentlich kein Blödsinn in diesen Dingen drin, ja? Gut. Jetzt aber.

Daniel Westheide Jetzt aber.

Sven Johann Genau, jetzt aber ich drücke den Knopf. Bis denne, schönen Tag noch.

Daniel Westheide Tschüss.

Jakob Oswald Ciao.

Principal Consultant

Jakob interessiert sich für alles rund um Software-Architektur, Entwicklungsprozesse und Organistionsstrukturen.

Senior Consultant

Daniel Westheide ist Senior Consultant bei INNOQ und entwickelt seit 2006 Server-Applikationen auf der JVM. Er interessiert sich besonders für funktionale Programmierung, Nachhaltigkeit und Systems Thinking. Er ist Autor mehrerer Scala-Lehrbücher.

Senior Consultant

Sven Johann ist Senior Consultant bei INNOQ und beschäftigt sich seit vielen Jahren mit der Modernisierung von mittleren und großen Java-Anwendungen. Er ist aktiver Teilnehmer verschiedener Workshops des Software Engineering Institutes (Managing Technical Debt) und des Leibnitz Zentrums für Informatik (Dagstuhl Seminar “Managing Technical Debt”). Zudem ist er Program Chair der GOTO Amsterdam und Show Host von Software Engineering Radio.