Mockito + PowerMock LinkageError while mocking system class
Asked Answered
C

7

201

I've got such a 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);
        ...
    }
}

While running this tests I got:

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)

Do you know how can I prevent this? I maybe there is another way to mock such a piece of code:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Crim answered 13/5, 2013 at 11:13 Comment(6)
What is it you're trying to mock? And why?Laminar
First test is getters and setters test, I'm calling constructor there (and there the exception occurs). The second one is constructor test. I want to get control of what resources enumeration contains in third code snippet.Crim
First of all, it looks to me that your tests are very tightly coupled to your implementation. By experience, this will lead to fragile tests. Preferrably, you want to think "black box" when writing your tests. "What is this piece of code supposed to do", rather than "How is this piece of code doing it". Second, I think you'd be better off just creating a set of resources and let the Java runtime deal with the classloading itself.Laminar
It is possible to create various sets of resources, as they where test cases?Crim
Sure. The easiest for you is probably to parametrize the name of the resources. Then you can pass different resource names into your tests.Laminar
You mean creating another method with new parameters? It's strange, but I'm writing tests for project that's already complete, but tests are missing, so it's not so easy in that case.Crim
L
491

Try adding this annotation to your Test class:

@PowerMockIgnore("javax.management.*")

Worked for me.

Lacker answered 21/1, 2014 at 20:6 Comment(4)
precision *"to your Test Class". Simple and usefull answer !Recitative
Can this be done by code or config as well? I could not find any way to do this. We have hundreds of tests ... i can not adjust them all.Technical
@FredericLeitenberger see my answer belowPachton
Can you please also explain the intution and meaning of this fix? What instruction are we giving to PowerMockito using that line?Hippel
B
39

Similar to the accepted response here, I ended up having to exclude all of the SSL related classes:

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

Adding that to the top of my class resolved the error.

Bearnard answered 22/7, 2016 at 18:31 Comment(4)
Still needed to add some more paths but you saved my life man! @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})Clowers
Good to know about com.sun too.Bearnard
I needed the following: @PowerMockIgnore({"javax.management.*", "javax.crypto.*"})Dancer
This one saved me: @PowerMockIgnore({"javax.management.*", "org.apache.http.*", "com.amazonaws.http.conn.ssl.*", "javax.net.ssl.*", "com.sun.*", "javax.xml.*", "javax.crypto.*"})Jost
S
29

Classloader conflict, use this: @PowerMockIgnore("javax.management.*")

Let mock classloader do not load javax.*. It works.

Snuffbox answered 14/4, 2014 at 2:10 Comment(1)
After using @PowerMockIgnore("javax.management.*"), the test class works well singly. But running as Junit test on that package got Failed to load ApplicationContext error. org.apache.catalina.LifecycleException: A child container failed during start and so on.Monseigneur
M
8

This may be a bit of an old topic, but I have also ran into this problem. Turns out that some of the java versions cannot handle powermockito when powermock finds out there are 2 classes with the same name in the same package (over different dependencies).

With any version higher than Java 7_25 it gives this error.

Magician answered 12/12, 2014 at 8:38 Comment(3)
"With any version higher than Java 7_25 it gives this error.", This is informative.Frivol
What it means: "cannot handle powermockito"? Is there any way to deal with it besides ignoring by annotation?Sweated
It's a long time ago, but I think we sorted it out by making sure there are not 2 classes with the same name in the same kind of package. Of course, if you have 2 libraries you depend on, and they reside in there... it is going to be difficult. I don't know if this issue has been fixed in the meantime.Magician
P
8

In PowerMock 1.7.0 a user-defined global configuration can be added to your project's classpath. PowerMockConfig

org/powermock/extensions/configuration.properties

Simply add a line in the properties file like:

powermock.global-ignore=javax.management.*

This will resolve the error for all the test classes in your project.

Pachton answered 3/1, 2019 at 23:41 Comment(1)
Note, spaces and double quotes are not allowed in the configuration.properties file. See: github.com/powermock/powermock/issues/989Alum
C
6

Depending on your individual setup, it may be necessary to add more methods to @PowerMockIgnore. I stumbled over this with slf4j, for using PowerMock and slf4j together, you'll need

@PowerMockIgnore({ "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*", "org.w3c.dom.*" })
Concepcion answered 3/2, 2021 at 12:6 Comment(1)
Compared to other examples of @PowerMockIgnore , this one worked for me, because of the "org.xml.*" entry. Others with this issue should check, which signature issues arise from PowerMock and Ignore those packages with the annotation.Robi
S
5

In order to mock system classes, prepare the class that is the target of the test, not Thread.class. There's no way PowerMock will be able to instrument Thread.class because it is required during JVM startup - well before PowerMock can instrument.

The way instrumentation works, once a class is loaded, it can no longer be intstrumented.

See the PowerMock wiki.

Sanderling answered 31/8, 2013 at 19:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.