JUnit 4 PermGen Größenüberlauf beim Ausführen von Tests in Eclipse und Maven2

8

Ich mache einige Komponententests mit JUnit, PowerMock und Mockito. Ich habe viele Testklassen mit @RunWith(PowerMockRunner.class) und @PrepareForTest(SomeClassesNames) kommentiert, um finale Klassen und mehr als 200 Testfälle zu spielen.

Vor kurzem habe ich ein Problem mit einem PermGen Space Overflow bekommen, wenn ich meine gesamte Testsuite in Eclipse oder Maven2 laufen lasse. Wenn ich meinen Test nacheinander durchführe, ist jeder erfolgreich.

Ich habe etwas darüber recherchiert, aber keiner der Ratschläge hat mir geholfen (ich habe PermGenSize und MaxPermSize erhöht). Kürzlich habe ich herausgefunden, dass es eine Klasse gibt, die nur statische Methoden enthält und jede Methode ein mit PowerMockito verspottetes Objekt zurückgibt. Ich frage mich, ob es eine gute Übung ist und vielleicht ist dies der Ursprung des Problems, weil statische Variablen zwischen Komponententests geteilt werden?

Allgemein gesprochen ist es eine gute Übung, eine statische Klasse mit vielen statischen Methoden zu haben, die statische mockige Objekte zurückgibt?

    
BlueLettuce16 27.07.2012, 07:23
quelle

3 Antworten

8

Wie @Brice sagt, werden die Probleme mit PermGen von Ihrem extensiven Gebrauch von verspotteten Objekten herrühren. Powermock und Mockito erstellen beide eine neue Klasse, die zwischen der verspotteten Klasse und Ihrem Testcode liegt. Diese Klasse wird zur Laufzeit erstellt und in PermGen geladen und (praktisch) nie wiederhergestellt. Daher Ihre Probleme mit dem PermGen-Raum.

Auf Ihre Frage:

1) Das Teilen von statischen Variablen wird als Code-Geruch betrachtet. Es ist in einigen Fällen notwendig, aber es führt Dependenzen zwischen den Tests ein. Test A muss vor Test B ausgeführt werden.

2) Die Verwendung von statischen Methoden, um ein verspottetes Objekt zurückzugeben, ist nicht wirklich ein Code-Geruch, es ist ein Attern, der oft verwendet wird. Wenn Sie Ihren Permgenraum wirklich nicht vergrößern können, haben Sie eine Reihe von Optionen:

Verwenden Sie einen Pool von Mocks mit PowerMock#reset() , wenn der Mock in den Pool zurückgelegt wird. Dies würde die Anzahl der Kreationen reduzieren, die du tust.

Zweitens haben Sie gesagt, dass Ihre Kurse endgültig sind. Wenn dies änderbar ist, können Sie einfach eine anonyme Klasse im Test verwenden. Dies verringert wiederum die Menge an verwendetem Permgenraum:

%Vor%

Drittens können Sie eine Schnittstelle einführen (verwenden Sie Refactor- & gt; Schnittstelle extrahieren in Eclipse), die Sie dann mit einer leeren Klasse erweitern, die nichts tut. Dann, in deiner Klasse, machst du ähnlich wie oben. Ich benutze diese Technik ziemlich oft, weil ich es leichter finde zu lesen:

%Vor%

dann in der Klasse:

%Vor%

Ich muss zugeben, dass ich kein besonderer Fan von Spott bin. Ich benutze es nur, wenn es nötig ist. Ich tendiere dazu, entweder die Klasse mit einer anonymen Klasse zu erweitern oder eine echte MockXXX-Klasse zu erstellen. Weitere Informationen zu diesem Gesichtspunkt finden Sie unter Verspotten und Testen von Ergebnissen. von Onkel Bob

By the way, in Maven todsichere, können Sie immer forkMode = immer welches den jvm für jede Testklasse ausgibt. Dies wird jedoch dein Eclipse-Problem nicht lösen.

    
Matthew Farwell 27.07.2012, 10:02
quelle
23

Ich bekomme auch PermGen-Fehler von Junit in Eclipse. Aber ich benutze keine spöttischen Bibliotheken wie Mockito oder EasyMock. Allerdings ist meine Codebasis groß und meine Junit-Tests verwenden Spring-Test (und sind intensive und komplexe Testfälle). Dafür muss ich das PermGen für alle meine Junit-Tests wirklich erhöhen.

Eclipse wendet die installierten JRE-Einstellungen auf die Junit-Läufe an - nicht auf die eclipse.ini-Einstellungen. Um diese zu ändern:

  • Fenster & gt; Einstellungen & gt; Java & gt; Installierte JREs
  • Wählen Sie die Standard-JRE, Bearbeiten ... Schaltfläche
  • zu Default VM hinzufügen Argumente: -XX: MaxPermSize = 196m

Mit dieser Einstellung können Junit-Tests die intensiveren Testfälle in Eclipse ausführen und OutOfMemoryError: PermGen vermeiden. Dies sollte auch ein geringes Risiko darstellen, da die meisten einfachen Junit-Tests nicht den gesamten Speicher zuweisen.

    
Jay Meyer 12.09.2013 22:00
quelle
5

Erstens: Mockito benutzt CGLIB, um Mocks zu erstellen, und PowerMock benutzt Javassist für andere Dinge, wie das Entfernen der letzten Marker. PowerMock lädt auch Klassen in einem neuen ClassLoader. CGLIB ist dafür bekannt, die permanente Generation zu essen (einfach google CGLIB PermGen finden) relevante Ergebnisse zu diesem Thema).

Es ist keine klare Antwort, da es auf Details Ihres Projekts ankommt:

  1. Wie Sie sagten, gibt es eine statische Hilfsklasse, ich weiß nicht, ob statische Variablen auch mit Mocks enthalten sind, ich kenne die Details Ihres Codes nicht, also ist das reine Vermutung, und andere Leser, die es tatsächlich besser wissen könnte mich korrigieren.

    Es könnte sein, dass der ClassLoader (und zumindest einige seiner Kinder), der diese statische Klasse geladen hat, über Tests hinweg am Leben erhalten wird - könnte wegen der Statik sein (die in lebt) Klassenbereich ) oder aufgrund einer Referenz irgendwo - das heißt, wenn der ClassLoader noch lebt ( ie nicht Müll gesammelt) werden seine geladenen Klassen nicht verworfen ie Die Klassen einschließlich der generierten Klassen befinden sich noch im PermGen .

  2. Diese Klassen können auch sehr groß sein. Wenn Sie viele dieser Klassen laden, kann dies für höhere PermGen-Werte relevant sein, insbesondere, da Powermock für jeden Test Klassen in einem neuen Classloader neu laden muss .

Wieder kenne ich die Details Ihres Projekts nicht, also rate ich nur, aber Ihr permanentes Generierungsproblem könnte entweder durch Punkt 1 oder Punkt 2 oder sogar beides verursacht werden.

Wie auch immer, im Allgemeinen würde ich ja sagen: Eine statische Klasse, die ein statisch verspottetes Objekt zurückgeben könnte, sieht hier wie eine schlechte Übung aus, wie es normalerweise im Produktionscode der Fall ist. Wenn es schlecht gemacht wird, kann es zu ClassLoader's leak führen (das ist eklig!).

In der Praxis habe ich Hunderte von Tests (nur mit Mockito) durchgeführt, ohne die Speicherparameter zu ändern und ohne dass die CGLIB-Proxies entladen wurden. Ich benutze keine statischen Komponenten wie die Mockito-API.

Wenn Sie eine Sun / Oracle JVM verwenden, können Sie diese Optionen ausprobieren, um zu verfolgen, was passiert:

-XX:+TraceClassLoading und -XX:+TraceClassUnloading oder -verbose:class

Ich hoffe, das hilft.

Außerhalb des Bereichs dieser Frage:

Persönlich mag ich es nicht, Powermock trotzdem zu benutzen, ich benutze es nur in Eckfällen z. B. zum Testen von nicht modifizierbarem Legacy-Code. Powermock ist zu aufdringlich imho, es muss für jeden Test einen neuen Classloader spawnen, um seine Taten auszuführen (Modifizieren des Bytecodes), man muss die Testklassen stark annotieren, um sich zu vergnügen, ... Meiner Meinung nach überwiegen für die übliche Entwicklung all diese kleinen Unannehmlichkeiten den Vorteil der Fähigkeit, Finalspiele zu spielen. Sogar Johan, der Autor von Powermock, hat mir einmal gesagt, dass er stattdessen Mockito weiterempfiehlt und Powermock für einen bestimmten Zweck hält.

Verstehen Sie mich nicht falsch: Powermock ist eine fantastische Technologie, die wirklich hilft, wenn Sie mit (schlecht) entwickeltem Legacy-Code umgehen müssen, den Sie nicht ändern können. Aber nicht für die tägliche Entwicklung, besonders wenn TDD praktiziert wird.

    
Brice 27.07.2012 09:14
quelle