Testbarkeit von Code testen

In einer kurzen Demonstration Session auf dem OOPSLA 2008 habe ich gestern ein interessantes Tool kennengelernt. Mit dem Testability Explorer von Google kann Java Code dahingehend untersucht werden, wie testable die Klassen sind. Der Testability Explorer wird von Misko Hevery von Google betreut, der auch die Session auf der OOPSLA gegeben hat.

Zusammengefasst ist der Testability Explorer ein Command Line Utility mit dem sich Packages innerhalb jar-Files scannen lassen. Für jede Klasse aus diesen Packages wird ein Score vergeben, der sich daran orientiert, wie gut sich die Klasse in einem Unit Test testen lässt. Anschliessend wird für alle Metriken ein HTML Report erstellt, in dem die vergebenen Scores und alle gefundenen Probleme einsehen lassen.

Der Score basiert nach Aussage von Hevery grob gesagt darauf, welche Anstrengungen notwendig sind um einzelne Methoden isoliert und mit Mock Objekten zu testen. In der Demonstration wurde die Funktionsweise des Testability Explorers an einem Servlet aus einer Bibliothek demonstriert, die Hevery selbst vor einigen Jahren implementiert hatte. In der Service Methode des Servlets, wurde der HttpServletRequest in einem grossen if-else Block und abhängig von verschiedenen Conditions an verschiedene andere Servlet Implementierungen delegiert. Dabei wurde jedesmal eine neue Instanz mit dem new Keyword erzeugt. Im ersten Testlauf hatte das angesprochene Servlet einen Testability Score von über 9000. Je höher der Score desto schwerer lässt sich der Code testen. Im ersten Schritt wurden alle lokalen Objekterzeugungen in Fields konvertiert und mehrere Constructors hinzugefügt um somit die Tür für beispielsweise Dependency Injection zu öffnen. Dieser Schritt brachte bereits eine deutliche Verbesserung der Testbarkeit.

In einem zweiten Refactoring wurde die Anzahl der statischen Methodenaufrufe minimiert, bzw. die Implementierung der aufgerufenen Klasse auf Instanzmethoden umgestellt. Gerade dann, wenn der Client gegen ein Interface arbeitet und dort Instanzmethoden aufruft, wird das Testen um einiges leichter. Ein weiteres Problem der Servlet Klasse war die Verwendung eines zu breiten Scopes der verwendeten Objekte. Es wurde ein Repository Objekt in das Servlet hineingegeben, auf dem dann eine getPool().getConnection() aufgerufen wurde um mit der Connection weiter zu arbeiten. Das Problem dabei ist, dass dabei sehr viel mehr Schritte notwenig sind um für das Connection Objekt einen Mock Ersatz bereitzustellen. Um eine MockConnection zu erhalten, müssen ebenfalls das Repository und der Pool ”gemocked” werden. Eine entsprechende Mitteilung lässt sich sehr schön im Testreport des Testability Explorers ablesen. Die Lösung für das Problem an dieser Stelle war die direkte Verwendung und Übergabe der Connection im Constructor anstelle des kompletten Repository Objekts.

Der Testability Explorer untersucht unter der Haube jedoch nicht nur die statischen Probleme im Sourcecode. Vielmehr wird der Java Bytecode untersucht um weiterhin Probleme durch inkorrekte Verwendung von statisch globalem Objekten zu finden. Wie das genau funktioniert, konnte Hevery jedoch aufgrund von Zeitmangel nicht vorstellen. Das Open Source Projekt wird auf code.google.com gehostet und steht jedem zur Verfügung. Das es noch nicht sehr weit verbreitet ist, kann man unter anderem daran erkennen, dass Plugins für IDE's und Continuous Integration Server fehlen. In einem unserer nächsten Projekte will ich den Testability Explorer jedoch austesten und in diesem Rahmen ein Hudson Plugin schreiben. Ich denke gerade in Projekten mit vielen Junior Entwicklern ohne ausreichende TDD Erfahrung, kann der Testability Explorer schon sehr früh Metriken liefern, wie gut sich der Code des Teams testen lässt. In der Regel stösst man nämlich erst sehr viel später auf Probleme der Testbarkeit. Nämlich dann wenn die Testabdeckung einzelner Klassen oder Pakete zu niedrig ist. Soweit wie Google zu gehen, die ein Check-In von Code zurückrollen, wenn die eingecheckten Klassen nicht testbar genug sind, werden wir in unserem Team aber nicht gehen.