Wir versuchen, unser System so zu gestalten, dass es testbar ist und größtenteils mit TDD entwickelt wird. Derzeit versuchen wir das folgende Problem zu lösen:
An verschiedenen Stellen müssen statische Hilfsmethoden wie ImageIO und URLEncoder (beide Standard-Java-API) sowie verschiedene andere Bibliotheken verwendet werden, die hauptsächlich aus statischen Methoden bestehen (wie die Apache Commons-Bibliotheken). Es ist jedoch äußerst schwierig, Methoden zu testen, die solche statischen Hilfsklassen verwenden.
Ich habe verschiedene Ideen, um dieses Problem zu lösen:
- Verwenden Sie ein Mock-Framework, das statische Klassen verspotten kann (wie PowerMock). Dies mag die einfachste Lösung sein, aber irgendwie fühlt es sich an, als würde man aufgeben.
- Erstellen Sie instanziierbare Wrapper-Klassen für alle diese statischen Dienstprogramme, damit sie in die Klassen eingefügt werden können, die sie verwenden. Das klingt nach einer relativ sauberen Lösung, aber ich befürchte, wir werden am Ende eine Menge solcher Wrapper-Klassen erstellen.
- Extrahieren Sie jeden Aufruf dieser statischen Hilfsklassen in eine Funktion, die überschrieben werden kann, und testen Sie eine Unterklasse der Klasse, die ich tatsächlich testen möchte.
Aber ich denke immer wieder, dass dies nur ein Problem sein muss, mit dem viele Menschen bei TDD konfrontiert sind - daher muss es bereits Lösungen für dieses Problem geben.
Was ist die beste Strategie, um Klassen, die diese statischen Helfer verwenden, testbar zu halten?
quelle
Antworten:
(Keine "offiziellen" Quellen hier, ich fürchte - es gibt keine Spezifikation, wie man gut testet. Nur meine Meinungen, die hoffentlich nützlich sein werden.)
Wenn diese statischen Methoden echte Abhängigkeiten darstellen, erstellen Sie Wrapper. Also für Dinge wie:
... ist es sinnvoll eine Schnittstelle zu erstellen.
Aber viele der Methoden in Apache Commons sollten wahrscheinlich nicht verspottet / gefälscht werden. Nehmen Sie zum Beispiel eine Methode, um eine Liste von Zeichenfolgen zusammenzufügen und ein Komma dazwischen einzufügen. Es hat keinen Sinn , diese zu verspotten - lassen Sie einfach den statischen Anruf seine normale Arbeit erledigen. Sie möchten oder müssen das normale Verhalten nicht ersetzen. Sie haben es nicht mit einer externen Ressource oder etwas zu tun, mit dem Sie nur schwer arbeiten können, sondern nur mit Daten. Das Ergebnis ist vorhersehbar und Sie möchten nie, dass es etwas anderes ist als das, was es Ihnen sowieso gibt.
Ich vermute, dass Sie, nachdem Sie alle statischen Aufrufe entfernt haben, die wirklich praktische Methoden mit vorhersehbaren, "reinen" Ergebnissen (wie Base64 oder URL-Codierung) sind, anstatt Einstiegspunkte in ein ganzes Durcheinander logischer Abhängigkeiten (wie HTTP) zu finden, dass es vollkommen ist Es ist praktisch, mit den echten Abhängigkeiten das Richtige zu tun.
quelle
Dies ist definitiv eine mit einer Meinung versehene Frage / Antwort, aber für das, was es wert ist, dachte ich, ich würde meine zwei Cent einwerfen. In Bezug auf den TDD-Stil ist Methode 2 definitiv der Ansatz, der dem Brief folgt. Das Argument für Methode 2 ist, dass Sie, wenn Sie jemals die Implementierung einer dieser Klassen ersetzen wollten - sagen wir eine
ImageIO
äquivalente Bibliothek -, dies tun könnten, ohne das Vertrauen in die Klassen zu verlieren, die diesen Code nutzen.Wie Sie bereits erwähnt haben, werden Sie, wenn Sie viele statische Methoden verwenden, am Ende eine Menge Wrapper-Code schreiben. Dies könnte auf lange Sicht keine schlechte Sache sein. In Bezug auf die Wartbarkeit gibt es sicherlich Argumente dafür. Persönlich würde ich diesen Ansatz bevorzugen.
Allerdings gibt es PowerMock aus einem bestimmten Grund. Es ist allgemein bekannt, dass das Testen mit statischen Methoden sehr schmerzhaft ist, weshalb PowerMock entwickelt wurde. Ich denke, Sie müssen Ihre Optionen dahingehend abwägen, wie viel Arbeit es sein wird, alle Ihre Helferklassen im Vergleich zur Verwendung von PowerMock zu beenden. Ich glaube nicht, dass es "aufgibt", PowerMock zu verwenden - ich habe nur das Gefühl, dass das Umschließen der Klassen Ihnen mehr Flexibilität in einem großen Projekt ermöglicht. Je mehr öffentliche Aufträge (Schnittstellen) Sie bereitstellen können, desto klarer wird die Trennung zwischen Absicht und Umsetzung.
quelle
Als Referenz für alle, die sich ebenfalls mit diesem Problem befassen und auf diese Frage stoßen, werde ich beschreiben, wie wir beschlossen haben, das Problem anzugehen:
Wir folgen grundsätzlich dem als # 2 angegebenen Pfad (Wrapper-Klassen für statische Dienstprogramme). Wir verwenden sie jedoch nur, wenn es zu komplex ist, dem Dienstprogramm die erforderlichen Daten zur Erzielung der gewünschten Ausgabe bereitzustellen (dh wenn wir die Methode unbedingt verspotten müssen).
Dies bedeutet, dass wir für ein einfaches Dienstprogramm wie Apache Commons keinen Wrapper schreiben müssen
StringEscapeUtils
(da die benötigten Zeichenfolgen leicht bereitgestellt werden können) und wir keine Mocks für statische Methoden verwenden (wenn wir glauben, dass es Zeit zum Schreiben ist) eine Wrapper-Klasse und verspotten dann eine Instanz des Wrappers).quelle
Ich würde diese Klassen mit Groovy testen . Groovy kann einfach zu jedem Java-Projekt hinzugefügt werden. Mit ihr können Sie die statischen Methoden ganz einfach verspotten. Ein Beispiel finden Sie unter Verspotten statischer Methoden mit Groovy .
quelle
Ich arbeite für eine große Versicherungsgesellschaft und unser Quellcode enthält bis zu 400 MB reine Java-Dateien. Wir haben die gesamte Anwendung entwickelt, ohne an TDD zu denken. Ab Januar dieses Jahres haben wir mit dem Testen der einzelnen Komponenten begonnen.
Die beste Lösung in unserer Abteilung war die Verwendung von Mock-Objekten auf einigen systemabhängigen JNI-Methoden (geschrieben in C). Daher konnten Sie die Ergebnisse nicht jedes Mal auf jedem Betriebssystem genau abschätzen. Wir hatten keine andere Möglichkeit, als verspottete Klassen und spezielle Implementierungen von JNI-Methoden zu verwenden, um jedes einzelne Modul der Anwendung für jedes von uns unterstützte Betriebssystem zu testen.
Aber es war sehr schnell und es hat bisher ganz gut funktioniert. Ich empfehle es - http://www.easymock.org/
quelle
Die Objekte interagieren miteinander, um ein Ziel zu erreichen, wenn Sie ein Objekt haben, das aufgrund der Umgebung schwer zu testen ist (ein Webservice-Endpunkt, ein Dao-Layer, der auf die Datenbank zugreift, Controller, die http-Anforderungsparameter verarbeiten), oder Sie möchten Ihr Objekt dann isoliert testen Sie verspotten diese Objekte.
Die Notwendigkeit, statische Methoden zu verspotten, ist ein schlechter Geruch. Sie müssen Ihre Anwendung objektorientierter gestalten, und statische Methoden des Dienstprogramms zum Testen von Einheiten verleihen dem Projekt keinen Mehrwert. Die Wrapper-Klasse ist je nach Situation ein guter Ansatz, aber versuchen Sie es um die Objekte zu testen, die die statischen Methoden verwenden.
quelle
Manchmal verwende ich Option 4
Etwas wie das.
Was mir an diesem Ansatz gefällt, ist, dass die Utility-Methoden statisch bleiben, was mir gerade recht ist, wenn ich versuche, die Klasse im gesamten Code zu verwenden.
quelle