Statische Aufrufe, finale Klassen, im Testcode erstellte Objekte: Es gibt nur wenige Dinge, mit denen einige der aktuellen Mocking-Frameworks nicht umgehen können. Mit leistungsstarken Ansätzen wie Bytecode-Instrumentierung oder benutzerdefinierten Klassenladern machen diese Bibliotheken Code, der zuvor ein No-Go war, für Unit-Tests zugänglich. Und das auf eine elegante und bequeme Art und Weise, die Entwicklern, die an 'Standard'-Mocking-Frameworks gewöhnt sind, vertraut vorkommen wird.
Vor kurzem habe ich einige Code-Beispiele aus einem Vergleich von Test-Frameworks veröffentlicht, den ich vor einiger Zeit durchgeführt habe, und einen Blogbeitrag über einige meiner Eindrücke und Gedanken geschrieben.
Die letzte Grenze überschreiten
In einem Abschnitt habe ich einige Bibliotheken erwähnt, die darauf abzielen, die "letzte Grenze" von TDD zu überschreiten. Sie ermöglichen es, Code wie den folgenden zu testen, der beispielsweise statische Aufrufe, finale Klassen und die Instanzierung von Kollaborateuren im zu testenden Code enthält. [java] public final class ServiceA { public void doBusinessOperationXyz(EntityX data) throws InvalidItemStatus { List<?> items = Database.find("select item from EntityY item where item.someProperty=?", data.getSomeProperty()); BigDecimal total = new ServiceB().computeTotal(items); data.setTotal(total); Database.save(data); } } public static final class ServiceB { ... [/java] Ich habe bemerkt, dass ich die Ansätze der beiden Frameworks, JEasyTest und Jmockit, ziemlich klobig fand. In der anschließenden Diskussion stellte ich erfreut fest, dass meine Beispiele ziemlich veraltet waren: Jmockit und PowerMock, ein "Mock-Erweiterungs"-Framework, das häufig in den Kommentaren erwähnt wird, sind jetzt viel praktischer, mit einer Syntax, die sehr nahe an der von z.B. EasyMock oder Mockito.1 gewohnten ist.
Die Gefahren eines unentdeckten Landes?
Sicherlich ist die Existenz von Jmockit oder PowerMock ein Tribut an die mächtigen Techniken, die über die JVM und die Java-Sprache und vor allem über die Java-Entwicklergemeinde verfügbar sind. Aber ich habe auch den Verdacht, dass die Flexibilität, die diese "Power Mocking"-Frameworks bieten, ohne sorgfältig überlegte Richtlinien schnell zu ebenso schlechtem wie (neu) testbarem Code führen könnte.
Kann ++Testbarkeit = --Qualität?
Die Testbarkeit wird häufig2 als eine der Fähigkeiten die gute Software anstreben sollte, und ich stimme dem sicherlich zu3. Wenn man einmal davon ausgeht, dass Testbarkeit ein hohes Maß an Unit-Tests4 voraussetzt, sind für mich zwei der wichtigsten Vorteile einer umfassenden Testsuite:
- als eine sich selbst verifizierende funktionale Beschreibung des Codes
- Sicherstellen, dass der Code unit-testbar ist, um Anti-Patterns zu vermeiden
Aus der Perspektive des ersten Punktes sind Power Mocking Frameworks zweifellos nützlich. Je mehr Code getestet und damit beschrieben werden kann, desto besser - es wird immer Fälle geben, die Standard-Mocks nicht adressieren können: Aufrufe von (z.B.) Thread.currentThread(), Legacy-Code5, Aufrufe von finalen Utility-Methoden, die teuer sein können, wie z.B. kryptografische Operationen usw. Es ist jedoch der zweite Aspekt, der mich zum Nachdenken gebracht hat: Wie ist die Beziehung zwischen testbarem und gutem Code, abgesehen von einem möglichen "intrinsischen" Wert der Testbarkeit? Unter Bezugnahme auf Miško Heverys häufig zitierte Liste der Alpträume der Testbarkeit wird deutlich, dass es offenbar erhebliche Überschneidungen zwischen Mustern, die die Testbarkeit von Code erschweren, und anerkannten Anti-Mustern gibt, die andere Aspekte der Softwarequalität beeinträchtigen. Ob es sich nun um statische Methoden handelt, die globale Zustände verraten, oder um im Code erzeugte Kollaborateure, die möglicherweise auf eng gekoppelte Systeme hindeuten, es sieht (zufällig?) so aus, als ob Code, der früher mit Standard-Mocking6 nur schwer oder gar nicht angreifbar war, wahrscheinlich auf eine grundlegendere Art und Weise fehlerhaft ist. Wir kehren zu unserem "kanonischen" Beispiel für schwer zu testenden Code7 zurück: [java] public final class ServiceA { public void doBusinessOperationXyz(EntityX data) throws InvalidItemStatus { Liste<?> items = Database.find("select item from EntityY item where item.someProperty=?", data.getSomeProperty()); BigDecimal total = new ServiceB().computeTotal(items); data.setTotal(total); Database.save(data); } } public static final class ServiceB { ... [/java] Meiner Meinung nach ist die Erstellung eines neuen kollaborierenden Dienstes nicht in erster Linie deshalb schlecht, weil er untestbar ist, sondern weil er eine Kopplung einführt und den Polymorphismus untergräbt, und das Gleiche gilt für die statischen Aufrufe der Klasse Database.
Wenn weniger mehr sein kann
Kurz gesagt, die Unfähigkeit, bestimmte Dinge mit Standard-Mocking-Frameworks zu erreichen, kann eine Art Segen in Verkleidung sein. Ihre Einschränkungen bedeuten, dass das Streben nach
- Wahrscheinlich gibt es noch weitere interessante "Power Mocking"-Frameworks, die hier nicht erwähnt sind. Wenn Sie eines kennen, hinterlassen Sie bitte einen Kommentar!
- Siehe hier oder hier oder hier, um nur einige zu nennen.
- Ich bin jedoch nicht der Meinung, dass "Testbarkeit" notwendigerweise "bequem, um Unit-Tests dafür zu schreiben" bedeuten muss. Mein Kollege Vincent Partington und ich diskutieren häufig darüber, ob es gerechtfertigt ist, einer Klasse einen Setter auf Paketebene hinzuzufügen, um das Objekt testbarer zu machen, wobei ich auf der Seite der Befürworter stehe. Auch wenn ich die @VisibleForTesting-Annotation von Google für einen wertvollen Hinweis für den Entwickler halte, bin ich der Meinung, dass sie eigentlich gar nicht notwendig sein sollte. Aber das ist ein Thema für einen anderen Blogbeitrag...
- Eine fragwürdige Annahme, natürlich!
- In einem Kommentar zum vorherigen Beitrag wurde ein interessanter Fall beschrieben, bei dem die Abhängigkeiten und Auswirkungen von nicht oder nur teilweise bekanntem Legacy-Code mithilfe von Power Mocking "rückwärts dokumentiert" wurden.
- Damit will ich nicht sagen, dass ich glaube, dass Power Mocking Frameworks die Verwendung von Statics usw. empfehlen, nur weil sie es ermöglichen, sie zu spötteln.
- Das ist ein etwas schwaches Argument, denn das Beispiel ist eindeutig nicht besonders realistisch.
Verfasst von
Andrew Phillips
Unsere Ideen
Weitere Blogs
Contact



