Vor knapp einer Woche habe ich mit meinem Blogeintrag Wie viel Sinn machen Unittests? – der im Rahmen der monatlichen Streitgespräche von Peter Bucher und mir erschienen ist – für einige Verwunderung gesorgt. Viele haben sich gefragt, warum gerade ich Unittests dermaßen skeptisch gegenüberstehe.
Ralf Westphal hat in seinem Kommentar vermutet, dass
etwas hinter den drei “problematischen Aspekten” das Grundproblem zu sein [scheint]
und ich muss zugeben, dass diese Vermutung nicht von der Hand zu weisen ist. Als problematisch habe ich an Unittests im Wesentlichen die Tatsache betrachtet, dass Code zum Zweck einer besseren Testbarkeit potenziell angepasst werden muss, da sich einige Konstrukte ansonsten nur schlecht oder gar nicht testen lassen.
Als Beispiel seien an dieser Stelle abstrakte Klassen angeführt, die per Definition nicht instanziiert und deshalb nicht direkt getestet werden können. Prinzipiell kann dieses Problem auf zwei Arten gelöst werden:
- Entweder werden die bereits bestehenden abgeleiteten Klassen getestet – immerhin ist die abstrakte Basisklasse in diesen implizit enthalten.
- Alternativ wird eine dedizierte abgeleitete Klasse erstellt, welche die abstrakte Basisklasse beerbt – die Basisklasse ist also auch in dieser Variante implizit enthalten.
Der relevante Unterschied zwischen beiden Varianten liegt in der Tatsache, dass die bereits bestehenden abgeleiteten Klassen eigene Funktionalität mitbringen, die den Test nicht unbedingt verfälschen, aber zumindest beeinflussen könnte: Es wird schwierig, den Test auf die abstrakte Basisklasse zu beschränken.
Eine dedizierte Klasse verfügt nicht über diese Nachteile: Sie sorgt lediglich dafür, dass die abstrakte Basisklasse in möglichst natürlicher Form instanziiert werden kann.
Technisch ist dieses Vorgehen unproblematisch, aber mental bereitete es mir Probleme: Seit Jahren wird Entwicklern vor allem in der akademischen Welt impliziert, dass Code nur dann “gut” ist, wenn er kompakt, elegant und im mathematischen Sinne schön ist.
Wie sind nun Änderungen, die nur um einer besseren Testbarkeit durchgeführt werden, mit diesem Paradigma vereinbar? Die kurze, aber schmerzhafte Antwort lautet: Gar nicht.
Wie bereits in Wie viel Sinn machen Unittests? angesprochen, beschreibt auch Roy Osherove in seinem Buch The Art of Unit Testing in dem Abschnitt Overcoming the encapsulation problem dieses Problem:
Some people feel that opening up the design to make it more testable is
a bad thing because it hurts the object-oriented principles the design is
based on. I can wholeheartedly say to those people, “Don’t be silly.”
Seine zugegebenermaßen sehr pragmatische Lösung
“Don’t be silly.”
dieses Problems stellt für mich keine zufriedenstellende Antwort dar – schließlich kommen die Bedenken nicht von ungefähr, und diese Antwort spiegelt dies in keinerlei Hinsicht wieder.
Auch die im Gespräch mit einigen anderen Entwicklern geäußerte Ansicht
Das ist halt so.
ist nicht zufriedenstellend. Diese Aussage beschreibt nämlich lediglich den Status Quo, begründet aber nicht, warum es in Ordnung oder vielleicht sogar gut ist, auf diese Art vorzugehen. Letztlich spiegelt diese Aussage lediglich das resignierte Abfinden mit einer – potenziell – unbequemen Tatsache dar.
Doch nun hat Ralf Westphal eine Begründung geliefert, die den Ansprüchen der Skeptiker – zumindest aus meiner Sicht – genügt:
das ist völlig ok. maschinen haben ja auch revisionsklappen für wartungsarbeiten. die haben sonst keine funktionalität. oder denk an kragsteine an alten kirchen, damit man gerüste anbringen kann.
Nach einigen Jahren des Zweifels und der Skepsis empfinde ich diese Analogie als eine wahre Erlösung – endlich wird begründet, warum ein solches Vorgehen in der Software in Ordnung ist.
Man mag einwenden, dass dieser Vergleich zur realen Welt doch offensichtlich und naheliegend sei, doch war in den vergangenen Jahren sonst niemand, mit dem ich mich über dieses Thema unterhalten habe, in der Lage, einen ähnlich klaren, einleuchtenden Vergleich zu ziehen.
Persönlich bemerke ich, dass diese Erkenntnis eine Art Wendepunkt darstellt – die mentale Barriere, mit der ich mich bislang gegen Unittests gesträubt habe, weil sie Anpassung von Code erfordern, ist aufgelöst. Alles, was bleibt, sind technische Probleme – und diese können mit Sicherheit gelöst werden.
Als Konsequenz werde ich das umsetzen, was Ralf Westphal vorgeschlagen und empfohlen hat:
Unit Tests […] konsequent und kompetent mal für einen Monat einzusetzen. Mit der Kompetenz mag es da schwierig sein, weil man sich dann eine Veränderung der Handlungs- und Denkgewohnheit selbst beibringen muss... aber das ist zumindest ein Anfang.
Wie es mir damit ergeht – darüber werde ich berichten …