Es ist eine gute Sache, daß ich mehr Unit Tests schreibe! Das hat zum einen damit zu tun, daß ich eine Menge gelernt habe; aber auch damit, daß ich viel GUI Code schreibe, und die GUI zum Testen zu starten ist extrem ineffizient.

Aber zum Thema private Methoden: Das Thema wurde für mich wieder aktuell, als ich vorgestern einen Vortrag von Andrew Glover zum Thema “Code Metrics for Targeted Code Refactoring” gehört habe (Organisiert von der Philadelphia JUG). Das Thema Unit Testing von privaten Methoden kam zur Sprache, und er erwähnte das JunitX Projekt, das dies ermöglicht.

Aber bevor wir ins Detail gehen, sollten wir uns die Frage stellen: Ist es überhaupt richtig, private Methoden zu testen, und was sind bessere Lösungen oder Alternativen?

Ich höre oft: “Wenn Du glaubst, private Methoden testen zu müssen, ist das ein Warnsignal.” Private Methoden sollten durch die nicht-privaten Methoden getestet werden, die sie benutzen. Sollte das nicht möglich sein, sollte die Architektur überdacht werden.

Ich stimme dem zu, daß diese Situationen mit Skepsis betrachtet werden sollten. Allerdings liegt meiner Meinung nach die Betonung von “Unit Test” auf “Unit”, und je kleiner die getestete Einheit, umso besser. Wenn ein Methode zu unübersichtlich wird (z.B. > 600 Zeilen), dann macht es Sinn, Teile in private Methoden auszulagern (und die Architektur zu überdenken, aber manchmal geht es eben nicht anders). Und meiner Meinung nach macht es viel Sinn, diese Teile einzeln zu prüfen. Und wenn wir diese kleinen Hilfsmethoden schreiben bevor die sie nutzende Methode geschrieben ist, dann macht es erst recht Sinn, diese zu testen. Bill Venners hat einen exellenten Aufsatz zu dem Thema geschrieben.

package private

Was können wir also tun? Das einfachste ist, die zu testenden Methoden package private zu machen. Eine package private Methode hat keine Qualifizierer (also weder private, protected oder public), und kann lediglich von Klassen in der selben Package gesehen werden. Vorteil: Extrem leicht zu implementieren; Refactoring (z.B. umbenennen) funktioniert problemlos, weil das Refactoringwerkzeug alle Referenzen finden kann. Nachteil: Wir verletzen Encapsulation. Das muß jeder für sich entscheiden, wie wichtig das ist. Aber ich finde es sehr wichtig. Zum einen gibt der Qualifizierer dem Entwickler ein starkes Signal und erlaubt, mit einem Blick die Bedeutung einer Methode einzuschätzen. Hinzu kommt, daß Eclipse mich warnt, wenn eine private Methode nicht benutzt wird. Das geht leider nicht mit package private Methoden.

Reflektion

JunitX benutzt Reflektion, um auf private Methoden zuzugreifen, und dieser Ansatz gefällt mir. Allerdings habe ich es nicht geschafft JunitX zum Laufen zu bringen (Version 5.1 schien noch nicht einmal richtig gejart worden zu sein, und 5.0 schien Bugs im Code zu haben). Aber es gibt ja genug Alternativen, und Junit Addons erfüllt diese Rolle – die Klasse junitx.util.PrivateAccessor, um genau zu sein. Diese Klasse ist sehr leicht zu nutzen. Nehmen wir mal an, wir wollen die folgende Methode testen:

 class RocketScience { private static boolean isPrime(int number) { ... } } 

Und hier ist der Test Code, der diese Methode testet:

 public void testIsPrimeExpectFalse() throws Throwable { Object isPrime = PrivateAccessor.invoke( RocketScience.class, "isPrime", new Class[] { Integer.TYPE }, new Object[] { new Integer(8) }); assertEquals(isPrime, Boolean.FALSE); } 

Diese Beispiel zeigt auch sehr schön, wie wir die Konvertierung von Primitives (int, boolean, etc.) zu Objekten durchführen, denn Reflektion funktioniert nur für Objekte. Natürlich würde man eine Methode wie diese in der Praxis nie Privat machen, sondern in einer Werkzeugklasse verfügbar machen – aber das ist ja nicht der Punkt von diesem Beispiel.

Die Sache hat einen einzigen Haken: Der Compiler (und die IDE) kann keine Korrelation zwischen diesem Test under der getesteten Methode erkennen, und wenn die Methode umbenannt wird, wird dieser Test nicht erkannt. Aber das ist in Ordnung – denn der Test wird versagen, und kann schnell und einfach repariert werden.

2 Comments
  1. Der Text hat mir gefallen. Ich stell mir auch oft solche Fragen und finde dann keine Antworten. Aber das Bild mit der Katzte hat mich schon irritiert ^^