Mockito + PowerMock LinkageError mientras se burla de la clase del sistema

166

Tengo un fragmento de código de este tipo:

@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);
        ...
    }
}

Mientras ejecutaba estas pruebas obtuve:

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)

¿Sabes cómo puedo evitar esto? Tal vez haya otra forma de burlarse de ese código:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Wojciech Reszelewski
fuente
¿De qué estás tratando de burlarte? ¿Y por qué?
NilsH
La primera prueba es getters y setters, estoy llamando al constructor allí (y allí ocurre la excepción). El segundo es la prueba del constructor. Quiero obtener el control de qué recursos contiene la enumeración en el tercer fragmento de código.
Wojciech Reszelewski
1
En primer lugar, me parece que sus pruebas están muy unidas a su implementación. Por experiencia, esto conducirá a pruebas frágiles. Preferiblemente, desea pensar en "recuadro negro" al escribir sus exámenes. "¿Qué se supone que debe hacer este código?", En lugar de "¿Cómo lo hace este código?". En segundo lugar, creo que sería mejor crear un conjunto de recursos y dejar que el tiempo de ejecución de Java se ocupe de la carga de clases.
NilsH
¿Es posible crear varios conjuntos de recursos, ya que son casos de prueba?
Wojciech Reszelewski
Por supuesto. Lo más fácil para usted es probablemente parametrizar el nombre de los recursos. Luego puede pasar diferentes nombres de recursos en sus pruebas.
NilsH

Respuestas:

408

Intente agregar esta anotación a su clase de prueba:

@PowerMockIgnore("javax.management.*")

Trabajó para mi.

Crandrad
fuente
2
precisión * "a su clase de prueba". Respuesta simple y útil!
pdem
3
¿Se puede hacer esto también por código o configuración? No pude encontrar ninguna manera de hacer esto. Tenemos cientos de pruebas ... no puedo ajustarlas todas.
Frederic Leitenberger
1
@FredericLeitenberger ver mi respuesta a continuación
user3474985
2
¿Puede explicar también la intuición y el significado de esta solución? ¿Qué instrucciones le estamos dando a PowerMockito usando esa línea?
Swapnil B.
33

Similar a la respuesta aceptada aquí, terminé teniendo que excluir todas las clases relacionadas con SSL:

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

Agregar eso a la parte superior de mi clase resolvió el error.

Jason D
fuente
55
Todavía necesitaba agregar algunos caminos más, pero me salvaste la vida, hombre @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})
Francisco López-Sancho
Es bueno saber acerca de com.sun también.
Jason D
1
Necesitaba lo siguiente: @PowerMockIgnore ({"javax.management. *", "Javax.crypto. *"})
Kristof Neirynck el
2
Este me salvó: @PowerMockIgnore ({"javax.management. *", "Org.apache.http. *", "Com.amazonaws.http.conn.ssl. *", "Javax.net.ssl. *" , "com.sun. *", "javax.xml. *", "javax.crypto. *"})
Fayaz Ahmed
26

Conflicto del cargador de clases , use esto:@PowerMockIgnore("javax.management.*")

Deje que el cargador de clases simulado no se cargue javax.*. Funciona.

烬 哥哥
fuente
Después de usar @PowerMockIgnore("javax.management.*"), la clase de prueba funciona bien individualmente. Pero correr como Junit testen ese paquete tiene un Failed to load ApplicationContexterror. org.apache.catalina.LifecycleException: A child container failed during starty así.
niaomingjian
8

Este puede ser un tema un poco viejo, pero también me he encontrado con este problema. Resulta que algunas de las versiones de Java no pueden manejar powermockito cuando powermock descubre que hay 2 clases con el mismo nombre en el mismo paquete (a través de diferentes dependencias).

Con cualquier versión superior a Java 7_25 da este error.

Rens Groenveld
fuente
2
"Con cualquier versión superior a Java 7_25 da este error". Esto es informativo.
Kajal Sinha
¿Qué significa: "no puede manejar powermockito"? ¿Hay alguna manera de lidiar con eso además de ignorarlo mediante anotaciones?
Línea
Hace mucho tiempo, pero creo que lo resolvimos asegurándonos de que no haya 2 clases con el mismo nombre en el mismo tipo de paquete. Por supuesto, si tiene 2 bibliotecas de las que depende y residen allí ... será difícil. No sé si este problema se ha solucionado mientras tanto.
Rens Groenveld
3

Para burlarse de las clases del sistema, prepare la clase que es el objetivo de la prueba, no Thread.class. No hay forma de que PowerMock pueda instrumentar Thread.classporque es necesario durante el inicio de JVM, mucho antes de que PowerMock pueda instrumentar.

La forma en que funciona la instrumentación, una vez que se carga una clase, ya no se puede instrumentar.

Ver el wiki de PowerMock .

ceniza
fuente
3

En PowerMock 1.7.0, se puede agregar una configuración global definida por el usuario al classpath de su proyecto. PowerMockConfig

org/powermock/extensions/configuration.properties

Simplemente agregue una línea en el archivo de propiedades como:

powermock.global-ignore=javax.management.*

Esto resolverá el error para todas las clases de prueba en su proyecto.

usuario3474985
fuente