Neben dem grundlegenden Mindset welches Clean-Code Software-Engineers haben sollten, ist es nun auch notwendig Grundlagen der Softwarequalität, des Testens und von Reviews und Unittesting zu verinnerlichen. Deswegen beschäftigen sich die nächsten Abschnitte nun mit diesen Themen. Zunächst definieren wir was Softwarequalität ist und welche Arten von Tests es gibt. Wir gehen genauer auf das Unittesting ein. Es werden Vor- und Nachteile die Unittesting mit sich bringt diskutiert. Außerdem beschäftigen wir uns mit einer wirtschaftlichen Betrachtung von Fehlern.
Definition Softwarequalität
Wer ist für die Qualität in einem Projekt verantwortlich?
Der Software-Engineer? Der Product-Owner? Der Requirements-Engineer?
In Wirklichkeit ist jeder der an der Entwicklung von Softwareprodukten beteiligt ist für die Qualität dieser Software verantwortlich.
Was ist Qualität überhaupt?
Qualität wird Laut DIN EN ISO 9000:2015-11 (der gültigen Norm zum Qualitätsmanagement) definiert als der „Grad, in dem ein Satz inhärenter Merkmale eines Objekts die Anforderungen erfüllt“. Nach einer anderen Norm der DIN EN ISO 8402:1995-08 ist Qualität „die Gesamtheit von Merkmalen einer Einheit bezüglich ihrer Eignung, festgelegte und vorausgesetzte Erfordernisse zu erfüllen.“ Einheiten bezeichnen dabei Produkte, Softwareprodukte, Dienstleistungen, Designs, Prozesse und Verfahren.
Somit wird Qualität von der ISO definiert als ein Maß von erfüllten Anforderungen eines Produkts. Werden die Anforderungen (= Requirements) besser und mehr erfüllt spricht man von besserer Qualität; werden sie schlechter erfüllt spricht man von schlechterer Qualität.
Bei Anforderungen gibt es auch unausgesprochene implizite Anforderungen die auch als Erwartungshaltungen bezeichnen werden können. So geht zum Beispiel ein Kunde, der eine Software entwickeln lässt, davon aus, dass das Software-Engineering-Team bei der Implementierung der Software Sicherheitsstandards einhält und auf Wartbarkeit, Erweiterbarkeit, Benutzbarkeit, Zuverlässigkeit, Effizienz und Übertragbarkeit achtet.
Was ist Qualität in einem Softwareprodukt?
In der ISO 9126 Norm wird Softwarequalität in sechs Qualitätsfelder eingeordnet:
- Funktionalität
- Zuverlässigkeit
- Benutzbarkeit
- Effizienz
- Änderbarkeit
- Übertragbarkeit

Soll eine Software also eine seht gute Qualität aufweisen, sodass man von einer hohen Softwarequalität spricht, dann muss das Softwareprodukt alle sechs Qualitätsfelder gut abdecken.
Und genau darin liegt oft ein Problem.
Software-Engineering Teams und Umsetzungsteams reden die meiste Zeit mit dem Kunden über die funktionale Anforderungen der Software, also Features. Die anderen oben genannten Eigenschaften der Softwarequalität werden meist weniger bis gar nicht behandelt sondern implizit angenommen.
Das Kano-Modell unterteilt Anforderungen in Basisanforderungen, Leistungsanforderungen und Begeisterungsanforderungen. Die Qualitätsfelder Zuverlässigkeit, Benutzbarkeit, Effizienz, Änderbarkeit und Übertragbarkeit werden dabei meist bei den Basisanforderungen und Leistungs-Anforderungen eingeordnet, während meist nur Funktionalität und teilweise Benutzbarkeit Begeisterungsfaktoren des Softwareprodukts sind.
Diese meist nicht explizit vom Kunden kommunizierten Anforderungen beeinflussen also genauso wie funktionale Anforderungen die Qualität des Softwareprodukts, sind aber oft vom Kunden nur implizit vorausgesetzt und nicht explizit in der Dokumentation und Planung des Umsetzungsteams zu finden. Daher braucht es eine eigene Kommunikation über diese Anforderungen, eine eigene Dokumentation dieser Anforderungen und nicht zuletzt eigene Tests dieser Anforderungen. Moderne agile Software-Engineering-Projekte adressieren dieses Thema mit unterschiedlichen Tests auf unterschiedlichen Ebenen.
Teststrategien und Testarten
Je nachdem welche Anforderungen getestet werden müssen, gibt es unterschiedliche Tests auf unterschiedlichen Ebenen eines Softwareprodukts/Softwareprojekts.
Auf der untersten granularsten Ebene werden Unittests geschrieben die kleinste Einheiten (=Units) des Sourcecodes testen.
Diese Units im Code sammeln sich zu größeren Funktionen, Klassen und Modulen zusammen, welche sich wiederum in das Gesamtsystem integrieren. Das Softwaresystem läuft wiederum in einer bestimmten Laufzeitumgebung, auf einem Betriebssystem oder in der Cloud, und letzten Endes verwenden User das System über eine Benutzeroberflächen und erweitern somit das System mental bis in die reale Welt. Daher braucht es auch auf all diesen Ebenen eigens angefertigte Tests.
Hierarchisch dargestellt bauen die Tests folgendermaßen auf einander auf:
- Acceptancetests
- Systemtests
- Integrationtests
- Unittests
Abb.: Hierarchische Darstellung der Testmethoden
Auf unterster Ebene bilden Unittests die Basis. Auf ihnen bauen die Integrationtests und darauf die Systemtests auf. Mit den Acceptancetests wird das Gesamtsystem mit Hilfe von Realusern komplett durchgetestet und somit überprüft, ob das System auch alle Anforderungen erfüllt.
Was ist Unittesting?
Unittesting ist die einfachste Art Software automatisiert zu testen. Dabei wird der Code in kleine Codeeinheiten (=Units) zerlegt und jede Unit einzeln für sich getestet. Das ist auch ein wichtiges Kernmerkmahl von Unittests, denn Unittests müssen für sich alleine stehen können und abgeschlossen sein. Das heißt, dass ein Unittest nur die kleinste Codeeinheit für sich abtestet und es keine Abhängigkeiten zu anderen Codeeinheiten gibt. Die Unit und der Unittest bauen also nicht auf anderen Units auf. Der Unittest stellt dann durch Überprüfungen (durch Assert-Statements beispielsweise) sicher, dass sich die zu überprüfende Unit genau so verhält, wie es in den Anforderungen definiert wurde und es keine Abweichungen von diesen Anforderungen gibt.
Kurzum:
Unittests validieren, dass jede Codeeinheit einer Software funktioniert wie es laut Anforderungen erwartet wird. |
Dabei werden die Unittests selbst vom Software-Engineer als Teil des Sourcecodes, während der Entwicklung des Softwareprodukts, geschrieben, ausgeführt und gewartet. Somit verstehen sich die Unittests als Teil des Sourcecodes des Softwareprojekts und sind der kleine Bruder der Codeeinheiten deren Funktionalität sie testen und abdecken. Eine Codeeinheit kann dabei eine für sich abgeschlossene Funktion oder Methode sein. Beim Test-Driven-Development (=TDD) werden zunächst aus den dokumentierten Anforderungen Unittests geschrieben, und erst dann die eigentliche Funktionalität der Codeeinheit programmiert. Die Eigenschaft, dass bei Unittests die Anforderungen der Software als Regeln im Test abgebildet sind, macht Unittests besonders wertvoll, denn dadurch können Fehler frühzeitig erkannt und verhindert werden und Änderungen am Sourcecode mit gutem Gewissen auch nach einiger Zeit durchgeführt werden. Selbst wenn der Software-Engineer bereits die Anforderungen nicht mehr hundertprozentig im Kopf hat, kann dieser Änderungen machen und sicherstellen, dass diese Änderungen nicht unabsichtlich bereits vorhandene Funktionalität zerstört. Dies ist auch ein riesiger Vorteil für das Team, wenn dieses immer wieder neue Teammitglieder bekommt, die somit nicht genau wissen wie das erwünschte Verhalten der Software sein soll. Unittests helfen somit bei Fluktuation von Teammitgliedern und beim Onboarding neuer Kollegen in das Projekt.
Der Unittesting-Irrglaube
Die Vorteile von Unittests liegen also klar auf der Hand. Doch leider lässt sich in der Praxis immer wieder feststellen, dass Software-Engineers keine oder zu wenig Unittests schreiben. Doch woran liegt das und was kann getan werden, um Unittesting im Projekt besser zu verankern?
Viele Software-Engineers glauben, dass ihnen das schreiben und warten von Unittests viel Zeit kostet und so passiert es immer wieder, dass einige Software-Engineers gerade in stressigeren Zeiten das schreiben von Unittests einstellen und nur mehr funktionalen Sourcecode schreiben. Das ist jedoch ein Trugschluss und ein Kompromiss zu Gunsten einer Zeiteinsparung sofort vs. den Konsequenzen dieser Zeiteinsparung und dem damit einhergehenden größeren Zeitverbrauch in Zukunft. Fehlende Unittests steigern das Risiko, dass Fehler entstehen und nicht entdeckt werden. Fehler verschleppen sich in spätere Phasen des Projekts. Neue Funktionalität baut auf fehlerhafter Funktionalität auf eine Abwärtsspirale entsteht.
Spätere Änderungen die, auf dem nicht durch Unittests abgedeckten Code, durchgeführt werden verändern ggf. die Funktionalität. Diese unbeabsichtigten Änderungen können aufgrund des fehlenden Unittests nicht entdeckt werden. Bereits vorhandene Funktionalität geht somit verloren.
Diese Fehler werden passieren und die Behebung dieser Fehler kostet wieder Zeit und Nerven, die Abwärtsspirale beginnt sich zu drehen. Hinzu kommt, dass Fehler die erst in späteren Projektphasen gefunden werden deutlich mehr Kosten verursachen. Denn die Zeit die in die Behebung dieser Kosten in späteren Phasen fließt, ist deutlich mehr, als wenn die Software gleich richtig geschrieben und getestet worden wäre.
Die Faustregel1 2 – „Rule of 10“ besagt, dass die Zeit, die für die Behebung eines Fehlers notwendig ist, als auch die Kosten die durch den Fehler entstehen, mit dem fortschreiten des Entwicklungsgrades um ein vielfaches höher sind als zu Beginn. Das heißt, je später ein Fehler gefunden wird, umso teurer wird es. Wird der Fehler beispielsweise in der Spezifizierung der Anforderungen gemacht und gleich dort gefunden entsteht nur ein minimaler Schaden. Wird der Fehler jedoch nicht gefunden und beim Design der Lösung oder erst in der Umsetzung vom Umsetzungsteam entdeckt, so steigen die Kosten für diesen Fehler und die Kosten für seine Behebung um das zehnfache oder sogar hundertfache an. Wird der Fehler erst im Produktivsystem entdeckt, so sind die Kosten und der Schade den dieser verursacht auf das tausendfache angestiegen und die Behebung ist sehr aufwendig und kostspielig.
Oft denken sich Software-Engineers jedoch, dass die Fehler, die durch die fehlenden Unittests nicht gefunden werden, sowieso bei den Integrationstests und Systemtests gefunden werden.
Doch selbst in agilen Umsetzungsmethoden, bei denen es defakto keine Phasen gibt, werden diese Tests erst dann gemacht, wenn die Funktionalität bereits fertig entwickelt wurde. Somit ist auch hier die „Rule of 10“ anwendbar.
Auch das vertrauen des Software-Engineering-Teams, dass die Fehler später gefunden werden ist leider sehr schlecht.
Studien belegen3 4, dass bei Unittests 25% aller Fehler gefunden werden, bei manuellen Funktionstests und bei Integrationstests jedoch auch nur 35 % bzw. nur 45% der vorhandenen Fehler gefunden werden. Das heißt aber, dass es eine schlechte Idee ist, auf die 25% vom Unittesting zu verzichten.
Denn werden die Testmethoden kombiniert, so addieren sich die erfolgreich gefundenen Fehler auf. Doch selbst wenn alle oben dargestellten Testmethoden verwendet werden, können 26,8125% der Fehler nicht durch die Testverfahren gefunden werden. Das heißt es müssen 26,8125% der Fehler in Systemtests und Acceptancetests gefunden werden, um eine weitgehend fehlerfreie Software zu bekommen.
Diese Tatsache zeigt also auch ganz klar auf, dass Testen an sich auch kein Allheilmittel ist. Nur eine Kombination aus allen Teststrategien und Code-Reviews führt zu einer sehr hohen Codequalität und zu stabilen Softwareprodukten. So zeigt sich1, dass in Design- und Code-Reviews durchschnittlich 55 – 60% der vorhandenen Fehler gefunden werden. Und nicht nur das, es wird auch darauf hingewiesen, dass Code-Reviews verschiedene Arten von Fehlern finden und Software-Engineers viel bedachtsamer mit Sourcecode umgehen, wenn dieser von Kollegen gereviewed wird.
Da mit Hilfe von Reviews bis zu 60% der Fehler bereits gefunden werden können, bevor diese manuellen Funktionstests und Integrationstests unterzogen werden, verringert sich die Anzahl der Fehler die nach den Integrationstests durch Systemtests und Acceptancetests gefunden werden müssen von 26,8125% auf 10,725 %
Es wäre also einerseits sehr schlecht Unittests komplett wegzulassen, genauso schlecht wäre es aber auch davon auszugehen dass Unittesting alle Fehler findet und somit Integrations- und Systemtests ersetzen und diese nicht mehr durchgeführt werden müssen.
Vorteile von Unittesting:
- Unittests sind Sourcecode
- Unittests können automatisiert durchgeführt werden
- Unittests bilden die Anforderungen ab
- Dokumentieren die Anforderungen
- Unittests können mit den herkömmlichen Tools eines Software-Engineer verwendet werden
- Helfen bei Fluktuation im Team
- Sind hilfreich beim Onboarding neuer Kollegen.
- Helfen bei Änderungen und sparen Zeit und Geld
Nachteile von Unittesting:
-
Unittests sind kein Ersatz für Code-Reviews
-
Unittests ersetzen keine Funktionstests, manuelle Tests, Integrationstests, oder Acceptancetests
-
Es besteht die Gefahr sich in falscher Sicherheit zu wiegen, wenn alle Unittests grün durchlaufen
-
Müssen, wie funktionaler Code auch, gewartet werden
Unittesting und Testabdeckung
Eine sehr bekannte und oft angewendete Qualitätskennzahl ist die Testabdeckung. Mittels Unittests lässt sich quasi jede Methode und jedes Statement durch Tests abdecken. Eine hohe Testabdeckung ist jedoch nicht immer das Ziel, denn nur weil alle Codezweige von Unittests durchlaufen und somit überprüft werden heißt dies nicht, dass diese Unittests sinnvoll und semantisch richtig sind. Eine hohe Anzahl an Unittests und eine hohe Testabdeckung bedeutet auch nicht, dass die Software Qualitativ hochwertiger und fehlerfrei ist, oder dieses Ziel erstrebenswert ist (siehe Kano-Modell und Definition Softwarequalität). Denn der Aufwand der mit erhöhter Qualität und Testabdeckung entsteht steigt steil an, deswegen sollten Fehler und Fehlerkosten vor allem auch wirtschaftlich betrachtet werden.
Wie aus der folgenden Grafik klar hervorgeht ist es notwendig ein Qualitätsoptimum zu finden, bei dem sich die Fehlerverhütungskosten und die Fehlerkosten bei einem optimalen Minimum einpendeln.
Diese Optimum ist auch für die Testabdeckung zu finden. Die Testabdeckung muss ausreichend hoch sein um die Fehlerkosten zu minimieren, darf aber nicht soweit getrieben werden dass Testfälle generiert werden die keinerlei Mehrwert für das Produkt und das Team darstellen und nur mehr für die Kennzahl geschrieben werden. Denn letzten Endes müssen auch Unittests als Teil der Codebasis gewartet werden und verursachen dementsprechend Kosten. Dieses Optimum muss aber von jedem Team selbst definiert und gefunden werden, denn es hängt stark vom jeweiligen Projekt ab.
Fazit
Saubere Software mit hoher Softwarequalität benötigt gute Tests. Teststrategien und Testmethoden bauen hierarchisch aufeinander auf. Die Basis dieser Tests sind Unittests. Alle Tests testen jedoch vor allem Funktionalitäten und widmen sich nicht den Qualitätsfeldern Änderbarkeit und Übertragbarkeit. Um diese Qualitätsfelder abzudecken müssen Code-Reviews durchgeführt werden und jedes Teammitglied muss auf Clean-Code achten und Code-Smells vermeiden, nur so entsteht ein hochqualitatives Softwareprodukt. Unittests sind aber die Basis dafür, dass das Software-Team sauber und effizient arbeiten kann, denn über Unittests werden Funktionalität und Anforderungen an die Software konserviert und dokumentiert. Dies hilft dem Team, dass keine Funktionalität durch Änderungen verloren geht. Bei Fluktuation oder zum Einschulen neuer Teammitglieder sind Unittests unabdingbar und geben den neuen Kollegen die Sicherheit, dass diese bei Änderungen nicht vorhandene Funktionalität unabsichtlich zerstören. Insgesamt dürfen aber beim Testen und vor allem bei der Testabdeckung die wirtschaftlichen Rahmenbedingungen nicht außer acht gelassen werden. Denn Unittests gehören zur Code-Basis und müssen ebenso wie funktionaler Sourcecode gewartet werden.
Quellen:
Das Kano-Modell der Kundenzufriedenheit: Reliabilität und Validität einer Methode zur Klassifizierung von Produkteigenschaften 29. März 2000 von Elmar Sauerwein
Qualitätsmanagement, 2. Aufl., 1996, S. 11. von T. Pfeifer
https://www.standishgroup.com/sample_research_files/RuleTen. pdf
https://www.olev.de/0/10er-regl.htm
https://www.guru99.com/unit-testing-guide.html
http://www.cesm.ucar.edu/working_groups/Software/dev_guide/dev_guide/node13.html
Code Complete: A Practical Handbook of Software Construction, von Steve McConnell
Programming Productivity. New York, New York: McGraw-Hill, 1986. von Jones, C.
3Programming Productivity. New York, New York: McGraw-Hill, 1986. von Jones, C.
4Code Complete: A Practical Handbook of Software Construction, von Steve McConnell
5Code Complete: A Practical Handbook of Software Construction, von Steve McConnell
Weitere Blogs zum Thema Softwarequalität:
Code-Smells: Object-Orientation Abusers |
Code-Smells: Couplers |
|
![]() |
![]() |
Weitere Buchempfehlungen
[Links zu Amazon]
![]() |
||
[Amazon 9,99€] | [Amazon 34,99 €] | [Amazon 24,95 €] |
[Amazon 34,00 €] | [Amazon 25,99 €] | [Amazon 42,00 €] |
Zum Autor:
David Theil aus Linz Oberösterreich ist Digitalisierungs-Coach, Software-Engineer und als Head of Software-Development für über 30 Softwareentwickler verantwortlich. Beruflich beschäftigt er sich bereits jahrelang mit der Digitalisierung und hat bereits bei vielen Digitalisierungs-Projekten in der Wirtschaft federführend mitgewirkt. Er bewegt sich in Themen wie Digitalisierung, IoT, oder Industrie 4.0 sowohl beratend als auch praktisch mit echten Lösungen.
https://www.xing.com/profile/David_Theil
https://www.linkedin.com/in/david-theil-1a4190148/
https://www.linkedin.com/groups/8678887