Warum verspottet Mockito keine statischen Methoden?

267

Ich habe hier einige Threads über statische Methoden gelesen und denke, ich verstehe die Probleme, die Missbrauch / übermäßiger Gebrauch statischer Methoden verursachen können. Aber ich bin nicht wirklich auf den Grund gegangen, warum es schwierig ist, statische Methoden zu verspotten.

Ich weiß, dass andere spöttische Frameworks wie PowerMock das können, aber warum kann Mockito das nicht?

Ich habe diesen Artikel gelesen , aber der Autor scheint religiös gegen das Wort zu sein static, vielleicht ist es mein schlechtes Verständnis.

Eine einfache Erklärung / Link wäre toll.

Abidi
quelle
12
Nur eine Randnotiz: PowerMock ist an sich keine Scheinobjektbibliothek, sondern fügt diese Funktionen (Verspottungsstatiken und Ctors) nur anderen Bibliotheken hinzu. Wir verwenden PowerMock + Mockito bei der Arbeit, sie schweben gut miteinander.
Matthias

Antworten:

238

Ich denke, der Grund könnte sein, dass Scheinobjektbibliotheken normalerweise Mocks erstellen, indem sie zur Laufzeit dynamisch Klassen erstellen (mit cglib ). Dies bedeutet, dass sie entweder zur Laufzeit eine Schnittstelle implementieren (das macht EasyMock, wenn ich mich nicht irre), oder sie erben von der Klasse zum Verspotten (das macht Mockito, wenn ich mich nicht irre). Beide Ansätze funktionieren nicht für statische Elemente, da Sie sie nicht mithilfe der Vererbung überschreiben können.

Die einzige Möglichkeit, die Statik zu verspotten, besteht darin , den Bytecode einer Klasse zur Laufzeit zu ändern , was vermutlich etwas aufwendiger ist als die Vererbung.

Das ist meine Vermutung, für was es wert ist ...

Matthias
quelle
7
Gleiches gilt übrigens auch für verspottete Konstruktoren. Auch diese können nicht durch Vererbung geändert werden.
Matthias
11
Es kann auch erwähnenswert sein, dass einige TDD / TBD-Befürworter das Fehlen statischer Methoden und Konstruktor-Verspottungen als eine gute Sache ansehen. Sie argumentieren, dass wenn Sie statische Methoden oder Konstruktoren verspotten müssen, dies ein Indikator für schlechtes Klassendesign ist. Wenn Sie beispielsweise beim Zusammenstellen Ihrer Codemodule einem puristischen IoC-Ansatz folgen, müssen Sie sich nicht einmal über Statiken oder Ctors lustig machen (es sei denn, sie sind natürlich Teil einer Black-Box-Komponente). Siehe auch giorgiosironi.blogspot.com/2009/11/…
Matthias
200
Ich denke, Spott-Tools sollten Ihnen das geben, was Sie brauchen, ohne davon auszugehen, dass sie wissen, was für Sie besser ist. Wenn ich beispielsweise eine Bibliothek eines Drittanbieters verwenden würde, die einen statischen Methodenaufruf verwendet, den ich verspotten musste, wäre es schön, dies tun zu können. Die Idee, dass ein Mock-Framework Ihnen keine Funktionen bietet, weil es als schlechtes Design angesehen wird, ist grundlegend fehlerhaft.
Lo-Tan
11
@ Lo-Tan - das ist so, als würde man sagen, dass eine Sprache zu allem fähig sein sollte, nicht vorausgesetzt, sie weiß es besser als Sie. Das ist nur Eitelkeit von Ihrer Seite, weil sie als imposant wirken. Das Problem hierbei ist, dass der "anti / pro statische" Kampf nicht klar ist, ebenso wie die Rahmenbedingungen. Ich bin damit einverstanden, dass wir beide haben sollten. Aber wo die Fakten sind klar, ziehe ich einen Rahmen, der erlegt diese Tatsachen. Das ist eine Art zu lernen - Tools, die Sie auf dem Laufenden halten. Sie müssen es also nicht selbst tun. Aber jetzt kann jeder Nudelkopf sein sogenanntes "gutes Design" durchsetzen. "Grundlegend fehlerhaft" ...
Nevvermind
13
@ Nevvermind Eh? Eine Hochsprache soll Ihnen helfen und die notwendigen Abstraktionen enthalten, damit Sie sich auf die wichtigen Teile konzentrieren können. Eine Testbibliothek ist ein Werkzeug - ein Werkzeug, mit dem ich qualitativ besseren und hoffentlich besser gestalteten Code produziere. Was bringt eine Test- / Scheinbibliothek, wenn sie Einschränkungen aufweist, die bedeuten können, dass ich sie nicht verwenden kann, wenn ich den schlecht gestalteten Code eines anderen integrieren muss? Scheint nicht gut durchdacht, wohingegen gute Sprachen gewesen sind .
Lo-Tan
28

Wenn Sie eine statische Methode verspotten müssen, ist dies ein starker Indikator für ein schlechtes Design. Normalerweise verspotten Sie die Abhängigkeit Ihrer zu testenden Klasse. Wenn sich Ihre zu testende Klasse auf eine statische Methode bezieht, wie z. B. java.util.Math # sin, bedeutet dies, dass die zu testende Klasse genau diese Implementierung benötigt (z. B. Genauigkeit vs. Geschwindigkeit). Wenn Sie von einer konkreten Sinus-Implementierung abstrahieren möchten, benötigen Sie wahrscheinlich eine Schnittstelle (sehen Sie, wohin diese führen wird)?

Jan.
quelle
3
Nun, ich habe statische Methoden verwendet, um Abstraktionen auf hoher Ebene bereitzustellen, beispielsweise eine "statische Persistenzfassade". Eine solche Fassade hält Client-Code von den Komplexitäten und Details einer ORM-API auf niedriger Ebene fern und bietet eine konsistentere und benutzerfreundlichere API bei gleichzeitig hoher Flexibilität.
Rogério
Und warum musst du dich darüber lustig machen? Wenn Sie von der statischen Methode abhängig sind, ist Ihre "Einheit" oder "Modul" nicht nur die Klasse, sondern enthält auch die "statische Persistenzfassade".
Jan
88
Stimmt, aber manchmal haben Sie möglicherweise keine Wahl, wenn Sie beispielsweise eine statische Methode verspotten müssen, die zu einer Klasse von Drittanbietern gehört.
Stijn Geukens
6
Stimmt, aber manchmal haben wir es mit Singletons zu tun.
Manu Manjunath
Der einzige Gedanke, der nicht durch Abstraktion gelöst werden kann, sind zu viele Abstraktionsebenen ... Das Hinzufügen von Abstraktionsebenen erhöht die Komplexität und ist häufig unnötig. Ich denke an die Beispiele, die ich für Frameworks gesehen habe, die versuchen, System.currentTimeMillis () zu verspotten, indem sie diesen einfachen Aufruf in eine einzelne Klasse einschließen. Am Ende haben wir eine Singleton-Klasse pro Methode, anstatt nur Methoden zu haben - nur um das Testen zu erleichtern. Und wenn Sie dann eine Drittanbieter-Dep einführen, die die statische Methode direkt anstatt über Ihren Singleton-Wrapper aufruft, schlagen die Tests trotzdem fehl ...
Pater Jeremy Krieg
5

Ich denke ernsthaft, dass es Code-Geruch ist, wenn Sie auch statische Methoden verspotten müssen.

  • Statische Methoden für den Zugriff auf allgemeine Funktionen? -> Verwenden Sie eine Singleton-Instanz und fügen Sie diese ein
  • Code von Drittanbietern? -> Wickeln Sie es in Ihre eigene Schnittstelle / Ihren eigenen Delegaten ein (und machen Sie es bei Bedarf auch zu einem Singleton)

Das einzige Mal, dass mir das übertrieben erscheint, sind Bibliotheken wie Guava, aber Sie sollten diese Art sowieso nicht verspotten müssen, weil sie Teil der Logik ist ... (Sachen wie Iterables.transform (..)) Auf
diese Weise Ihr eigener Code bleibt sauber, Sie können alle Ihre Abhängigkeiten auf saubere Weise verspotten und Sie haben eine Antikorruptionsschicht gegen externe Abhängigkeiten. Ich habe PowerMock in der Praxis gesehen und alle Klassen, für die wir es brauchten, waren schlecht gestaltet. Auch die Integration von PowerMock verursachte zeitweise schwerwiegende Probleme
(z. B. https://code.google.com/p/powermock/issues/detail?id=355) ).

PS: Gleiches gilt auch für private Methoden. Ich denke nicht, dass Tests über die Details privater Methoden Bescheid wissen sollten. Wenn eine Klasse so komplex ist, dass sie versucht, private Methoden zu verspotten, ist dies wahrscheinlich ein Zeichen, um diese Klasse aufzuteilen ...

pete83
quelle
2
Singleton wird Sie auf alle möglichen Probleme stoßen, insbesondere wenn Sie feststellen, dass Sie tatsächlich mehr als eine Instanz benötigen und jetzt Ihr gesamtes System umgestalten müssen, um dies zu erreichen.
Ricardo Freitas
Ich habe nicht gesagt, dass ich das Singleton-Muster jedem empfehlen kann. Ich meinte, wenn ich mich zwischen einer statischen Dienstprogrammklasse und einem Singleton mit derselben Funktionalität entscheiden muss, würde ich den Singleton wählen. Und ob eine Klasse ein Singleton ist oder nicht, sollte sowieso vom DI-Framework gesteuert werden, in meiner Klasse I @Inject SomeDependencyund in meiner Konfiguration definiere ich bind(SomeDependency.class).in(Singleton.class). Wenn es also morgen kein Singleton mehr ist, ändere ich die eine Konfiguration und das wars.
Pete83
@ pete83 Ich höre dich Bruder. Ich habe jedoch ein Problem mit Testbibliotheken oder Frameworks, bei denen Entwickler ihr Design ändern müssen, um das Design / die Grenzen des Testframeworks zu erfüllen. Das heißt, IMO stellt den Karren vor das Pferd oder der Schwanz wedelt mit dem Hund.
Matt Campbell
1
Dieses Argument macht für mich wenig Sinn. Singleton-Muster sind seit Jahren in Ungnade gefallen, aus zu vielen Gründen, um sie hier aufzulisten. Was macht "sauberen" Code aus? Wenn ich eine Klasseninstanzmethode habe, die eine statische Hilfsmethode aufruft, die eine E / A-Operation zurückgibt, warum sollte ich das nicht in einem Test verspotten lassen? Und wie ist das schlechte Design? All diese Handarbeit, die sich über verspottende statische Methoden lustig macht, summiert sich nicht. Das Verspotten einer Methode ist das Gegenteil vom Testen. Wenn es zu schwer zu implementieren ist, sagen Sie das einfach und fertig
Eggmatters
Oh Mann, ich habe nie über dieses Singleton-Muster der alten Schule gesprochen, bei dem jeder Foo.getInstance()überall anruft . Ich habe gerade Singleton in die Antwort geschrieben, um dem Argument entgegenzuwirken, "aber eine statische Methode erfordert nicht die Erstellung vieler Wrapper-Objekte". Auch konzeptionell gibt es für mich kaum einen Unterschied zwischen einer statischen Methode und einer Instanzmethode für einen Singleton, nur dass Sie diesen Singleton-Kollaborateur nicht verspotten können. Aber Singleton oder nicht ist absolut nicht der Punkt, den ich anstrebte. Es geht darum, Kollaborateure zu injizieren und zu verspotten und keine statischen Methoden aufzurufen, wenn dies das Testen erschwert.
pete83
4

Mockito gibt Objekte zurück, aber statisch bedeutet "Klassenebene, nicht Objektebene". Mockito gibt also eine Nullzeigerausnahme für statisch.

Salsinga
quelle
0

In einigen Fällen kann es schwierig sein, statische Methoden zu testen, insbesondere wenn sie verspottet werden müssen, weshalb die meisten verspotteten Frameworks sie nicht unterstützen. Ich fand diesen Blog-Beitrag sehr nützlich, um zu bestimmen, wie man statische Methoden und Klassen verspottet.

Tyler Treat
quelle
1
Das Verspotten statischer Methoden ist noch einfacher als das Verspotten von Instanzmethoden (da es keine Instanz gibt), wenn eine geeignete Verspottungs-API verwendet wird.
Rogério
Dies ist wie die Beantwortung der Frage mit der Frage selbst, weshalb es schwierig ist, dies zu tun, auf die dies keine Antwort ist.
Matthias
40
Ich habe es abgelehnt, weil der Blog-Beitrag eine kostspielige Problemumgehung (Umgestaltung des Produktionscodes) empfiehlt, anstatt das Problem der Isolierung einer Klasse von den statischen Methoden, die sie zufällig verwendet , tatsächlich zu lösen . IMO, ein spöttisches Werkzeug, das wirklich seine Arbeit erledigt, würde Methoden jeglicher Art nicht diskriminieren. Ein Entwickler sollte frei entscheiden können, ob die Verwendung statischer Methoden in einer bestimmten Situation gut oder schlecht ist, anstatt auf einen Pfad gezwungen zu werden.
Rogério