Mockito + PowerMock LinkageError ao zombar da classe do sistema

166

Eu tenho um trecho de código:

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

Durante a execução desses testes, obtive:

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)

Você sabe como posso evitar isso? Talvez eu exista outra maneira de zombar de um pedaço de código:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Wojciech Reszelewski
fonte
O que você está tentando zombar? E porque?
NilsH
O primeiro teste é o de getters e setters, estou chamando o construtor de lá (e a exceção ocorre). O segundo é o teste do construtor. Quero obter o controle de quais recursos a enumeração contém no terceiro trecho de código.
Wojciech Reszelewski
1
Primeiro de tudo, parece-me que seus testes estão intimamente associados à sua implementação. Pela experiência, isso levará a testes frágeis. De preferência, você deseja pensar em "caixa preta" ao escrever seus testes. "O que esse código deve fazer", em vez de "Como esse código está fazendo isso". Segundo, acho que seria melhor criar um conjunto de recursos e deixar o tempo de execução do Java lidar com o próprio carregamento de classe.
NilsH
É possível criar vários conjuntos de recursos, pois onde estão os casos de teste?
Wojciech Reszelewski
Certo. O mais fácil para você é provavelmente parametrizar o nome dos recursos. Em seguida, você pode passar nomes de recursos diferentes para seus testes.
NilsH

Respostas:

408

Tente adicionar esta anotação à sua classe de teste:

@PowerMockIgnore("javax.management.*")

Trabalhou para mim.

Crandrad
fonte
2
precisão * "para sua classe de teste". Resposta simples e útil!
Pdem
3
Isso também pode ser feito por código ou configuração? Não consegui encontrar nenhuma maneira de fazer isso. Temos centenas de testes ... não consigo ajustá-los todos.
Frederic Leitenberger
1
@FredericLeitenberger veja minha resposta abaixo
user3474985
2
Você também pode explicar a intuição e o significado dessa correção? Que instrução estamos dando ao PowerMockito usando essa linha?
Swapnil B.
33

Semelhante à resposta aceita aqui, acabei tendo que excluir todas as classes relacionadas ao SSL:

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

Adicionar isso ao topo da minha classe resolveu o erro.

Jason D
fonte
5
Ainda precisava adicionar mais alguns caminhos, mas você salvou minha vida, cara! @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})
Francisco López-Sancho
É bom saber sobre com.sun também.
Jason D #
1
Eu precisava o seguinte: @PowerMockIgnore ({ "javax.management *.", "Javax.crypto *."})
Kristof Neirynck
2
Este me salvou: @PowerMockIgnore ({"javax.management. *", "Org.apache.http. *", "Com.amazonaws.http.conn.ssl. *", "Javax.net.ssl. *" , "com.sun. *", "javax.xml. *", "javax.crypto. *"})
Fayaz Ahmed
26

Conflito do carregador de classe , use este:@PowerMockIgnore("javax.management.*")

Vamos classloader simulada não carregue javax.*. Ele funciona.

烬 哥哥
fonte
Após o uso @PowerMockIgnore("javax.management.*"), a classe de teste funciona bem isoladamente. Mas rodar como Junit testnesse pacote teve Failed to load ApplicationContexterro. org.apache.catalina.LifecycleException: A child container failed during starte assim por diante.
Niaomingjian
8

Isso pode ser um pouco antigo, mas também deparei-me com esse problema. Acontece que algumas das versões java não conseguem lidar com o powermockito quando o powermock descobre que existem 2 classes com o mesmo nome no mesmo pacote (em dependências diferentes).

Com qualquer versão superior ao Java 7_25, ocorre esse erro.

Rens Groenveld
fonte
2
"Com qualquer versão superior ao Java 7_25, ocorre este erro.", Isso é informativo.
Kajal Sinha
O que significa: "não pode lidar com powermockito"? Existe alguma maneira de lidar com isso além de ignorar pela anotação?
Linha
Há muito tempo, mas acho que resolvemos isso, garantindo que não haja duas classes com o mesmo nome no mesmo tipo de pacote. Obviamente, se você tem duas bibliotecas das quais depende, e elas residem lá ... será difícil. Não sei se esse problema foi corrigido nesse meio tempo.
Rens Groenveld
3

Para zombar das classes do sistema, prepare a classe que é o alvo do teste, não Thread.class. Não há como o PowerMock instrumentar Thread.classporque é necessário durante a inicialização da JVM - muito antes do PowerMock instrumentar.

O funcionamento da instrumentação, uma vez que uma classe é carregada, não pode mais ser instrumentada.

Veja o wiki do PowerMock .

cinza
fonte
3

No PowerMock 1.7.0, uma configuração global definida pelo usuário pode ser adicionada ao caminho de classe do seu projeto. PowerMockConfig

org/powermock/extensions/configuration.properties

Basta adicionar uma linha no arquivo de propriedades como:

powermock.global-ignore=javax.management.*

Isso resolverá o erro para todas as classes de teste em seu projeto.

user3474985
fonte