Mockito + PowerMock LinkageError beim Verspotten der Systemklasse

165

Ich habe so ein Code-Snippet:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Thread.class})
public class AllMeasuresDataTest {

@Before
public void setUp() throws Exception {
}

@Test
public void testGetMeasures() {
    AllMeasuresData measure = new AllMeasuresData();
    assertEquals(measure.getMeasures(), null);
    HashMap<String, Measure> map = new HashMap<String, Measure>();
    measure.setMeasures(map);
    assertEquals(measure.getMeasures(), map);
    measure.setMeasures(null);
    assertEquals(measure.getMeasures(), null);
}

@Test
public void testAllMeasuresData() throws IOException {
    ClassLoader loader = PowerMockito.mock(ClassLoader.class);
    Thread threadMock = PowerMockito.mock(Thread.class);
    Vector<URL> vec = new Vector<URL>();
    Mockito.when(loader.getResources("measure")).thenReturn(vec.elements());
    Mockito.when(threadMock.getContextClassLoader()).thenReturn(loader);
    PowerMockito.mockStatic(Thread.class);
    Mockito.when(Thread.currentThread()).thenReturn(threadMock);
        ...
    }
}

Während ich diese Tests durchführte, bekam ich:

java.lang.LinkageError: loader constraint violation: loader (instance of org/powermock/core/classloader/MockClassLoader) previously initiated loading for a different type with name "javax/management/MBeanServer"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at org.powermock.core.classloader.MockClassLoader.loadUnmockedClass(MockClassLoader.java:201)
at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:149)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:67)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at org.codecover.instrumentation.java.measurement.ProtocolImpl.initializeMBean(ProtocolImpl.java:247)
at org.codecover.instrumentation.java.measurement.ProtocolImpl.<init>(ProtocolImpl.java:237)
at org.codecover.instrumentation.java.measurement.ProtocolImpl.getInstance(ProtocolImpl.java:185)
at measure.CodeCoverCoverageCounter$6ya5ud0ow79ijrr1dvjrp4nxx60qhxeua02ta2fzpmb1d.<clinit>(MeasureCalculatorsHolder.java:146)
at measure.MeasureCalculatorsHolder.<clinit>(MeasureCalculatorsHolder.java:17)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:188)
at javassist.runtime.Desc.getClassObject(Desc.java:43)
at javassist.runtime.Desc.getClassType(Desc.java:152)
at javassist.runtime.Desc.getType(Desc.java:122)
at javassist.runtime.Desc.getType(Desc.java:78)
at algorithm.AllMeasuresDataTest.testGetMeasures(AllMeasuresDataTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTestInSuper(PowerMockJUnit49RunnerDelegateImpl.java:116)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTest(PowerMockJUnit49RunnerDelegateImpl.java:77)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.codecover.juniteclipse.runner.EclipseTestRunner.main(EclipseTestRunner.java:40)

Wissen Sie, wie ich das verhindern kann? Vielleicht gibt es einen anderen Weg, einen solchen Code zu verspotten:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Wojciech Reszelewski
quelle
Was versuchst du zu verspotten? Und warum?
NilsH
Der erste Test ist der Getter- und Setter-Test. Ich rufe dort den Konstruktor auf (und dort tritt die Ausnahme auf). Der zweite ist der Konstruktortest. Ich möchte die Kontrolle darüber erhalten, welche Ressourcenaufzählung im dritten Codefragment enthalten ist.
Wojciech Reszelewski
1
Zunächst scheint es mir, dass Ihre Tests sehr eng mit Ihrer Implementierung verbunden sind. Erfahrungsgemäß führt dies zu fragilen Tests. Am besten möchten Sie beim Schreiben Ihrer Tests an "Black Box" denken. "Was soll dieser Code tun?" Anstatt "Wie macht dieser Code das?". Zweitens denke ich, dass Sie besser dran sind, wenn Sie nur eine Reihe von Ressourcen erstellen und die Java-Laufzeit das Laden der Klasse selbst übernehmen lassen.
NilsH
Es ist möglich, verschiedene Sätze von Ressourcen zu erstellen, da sie Testfälle sind?
Wojciech Reszelewski
Sicher. Am einfachsten ist es für Sie wahrscheinlich, den Namen der Ressourcen zu parametrisieren. Anschließend können Sie verschiedene Ressourcennamen an Ihre Tests übergeben.
NilsH

Antworten:

407

Versuchen Sie, diese Anmerkung zu Ihrer Testklasse hinzuzufügen:

@PowerMockIgnore("javax.management.*")

Hat für mich gearbeitet.

Crandrad
quelle
2
Präzision * "zu Ihrer Testklasse". Einfache und nützliche Antwort!
pdem
3
Kann dies auch per Code oder Konfiguration erfolgen? Ich konnte keinen Weg finden, dies zu tun. Wir haben Hunderte von Tests ... ich kann sie nicht alle anpassen.
Frederic Leitenberger
1
@FredericLeitenberger siehe meine Antwort unten
user3474985
2
Können Sie bitte auch die Absicht und Bedeutung dieses Fixes erläutern? Welche Anweisungen geben wir PowerMockito über diese Leitung?
Swapnil B.
33

Ähnlich wie bei der hier akzeptierten Antwort musste ich schließlich alle SSL-bezogenen Klassen ausschließen:

@PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.http.conn.ssl.*", "javax.net.ssl.*"})

Das Hinzufügen zum Anfang meiner Klasse hat den Fehler behoben.

Jason D.
quelle
5
Ich musste noch einige weitere Pfade hinzufügen, aber du hast mein Leben gerettet, Mann! @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})
Francisco López-Sancho
Gut zu wissen, auch über com.sun.
Jason D
1
Ich brauchte folgendes: @PowerMockIgnore ({"javax.management. *", "Javax.crypto. *"})
Kristof Neirynck
2
Dieser hat mich gerettet: @PowerMockIgnore ({"javax.management. *", "Org.apache.http. *", "Com.amazonaws.http.conn.ssl. *", "Javax.net.ssl. *" , "com.sun. *", "javax.xml. *", "javax.crypto. *"})
Fayaz Ahmed
26

Classloader- Konflikt, verwenden Sie Folgendes :@PowerMockIgnore("javax.management.*")

Lassen Sie den Mock Classloader nicht laden. javax.*. Es funktioniert.

烬 哥哥
quelle
Nach der Verwendung @PowerMockIgnore("javax.management.*")funktioniert die Testklasse einzeln gut. Aber das Laufen wie Junit testauf diesem Paket hat einen Failed to load ApplicationContextFehler bekommen. org.apache.catalina.LifecycleException: A child container failed during startund so weiter.
Niaomingjian
8

Dies mag ein altes Thema sein, aber ich bin auch auf dieses Problem gestoßen. Es stellt sich heraus, dass einige der Java-Versionen powermockito nicht verarbeiten können, wenn powermock feststellt, dass sich zwei Klassen mit demselben Namen im selben Paket befinden (über verschiedene Abhängigkeiten hinweg).

Bei jeder höheren Version als Java 7_25 wird dieser Fehler ausgegeben.

Rens Groenveld
quelle
2
"Bei jeder höheren Version als Java 7_25 wird dieser Fehler ausgegeben.", Dies ist informativ.
Kajal Sinha
Was es bedeutet: "Kann nicht mit Powermockito umgehen"? Gibt es eine Möglichkeit, damit umzugehen, außer sie durch Anmerkungen zu ignorieren?
Linie
Es ist lange her, aber ich denke, wir haben es geklärt, indem wir sichergestellt haben, dass es nicht zwei Klassen mit demselben Namen in derselben Art von Paket gibt. Wenn Sie zwei Bibliotheken haben, auf die Sie angewiesen sind und die sich dort befinden, wird es natürlich schwierig. Ich weiß nicht, ob dieses Problem in der Zwischenzeit behoben wurde.
Rens Groenveld
3

Um Systemklassen zu verspotten, bereiten Sie die Klasse vor, die das Ziel des Tests ist, nicht Thread.class. PowerMock kann auf keinen Fall instrumentieren, Thread.classda dies während des JVM-Starts erforderlich ist - lange bevor PowerMock instrumentieren kann.

Die Funktionsweise der Instrumentierung kann nach dem Laden einer Klasse nicht mehr instrumentiert werden.

Siehe das PowerMock-Wiki .

Asche
quelle
3

In PowerMock 1.7.0 kann dem Klassenpfad Ihres Projekts eine benutzerdefinierte globale Konfiguration hinzugefügt werden. PowerMockConfig

org/powermock/extensions/configuration.properties

Fügen Sie einfach eine Zeile in die Eigenschaftendatei ein, wie:

powermock.global-ignore=javax.management.*

Dadurch wird der Fehler für alle Testklassen in Ihrem Projekt behoben.

user3474985
quelle