Mockito + PowerMock LinkageError lors de la moquerie de la classe système

166

J'ai un tel extrait de code:

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

En exécutant ces tests, j'ai obtenu:

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)

Savez-vous comment puis-je éviter cela? Il existe peut-être une autre façon de se moquer d'un tel morceau de code:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Wojciech Reszelewski
la source
Qu'est-ce que vous essayez de vous moquer? Et pourquoi?
NilsH
Le premier test est le test des getters et des setters, j'appelle le constructeur ici (et là, l'exception se produit). Le second est le test du constructeur. Je veux avoir le contrôle de ce que l'énumération des ressources contient dans le troisième extrait de code.
Wojciech Reszelewski
1
Tout d'abord, il me semble que vos tests sont très étroitement liés à votre implémentation. Par expérience, cela conduira à des tests fragiles. De préférence, vous voulez penser "boîte noire" lors de la rédaction de vos tests. "Qu'est-ce que ce morceau de code est censé faire", plutôt que "Comment ce morceau de code le fait-il". Deuxièmement, je pense que vous feriez mieux de simplement créer un ensemble de ressources et de laisser le runtime Java s'occuper du chargement de classe lui-même.
NilsH
Il est possible de créer divers ensembles de ressources, car ils où des cas de test?
Wojciech Reszelewski
Sûr. Le plus simple pour vous est probablement de paramétrer le nom des ressources. Ensuite, vous pouvez passer différents noms de ressources dans vos tests.
NilsH

Réponses:

408

Essayez d'ajouter cette annotation à votre classe Test:

@PowerMockIgnore("javax.management.*")

A travaillé pour moi.

Crandrad
la source
2
précision * "à votre classe de test". Réponse simple et utile!
pdem
3
Cela peut-il également être fait par code ou par configuration? Je n'ai trouvé aucun moyen de le faire. Nous avons des centaines de tests ... je ne peux pas tous les ajuster.
Frederic Leitenberger
1
@FredericLeitenberger voir ma réponse ci
user3474985
2
Pouvez-vous s'il vous plaît expliquer également l'intution et la signification de ce correctif? Quelles instructions donnons-nous à PowerMockito en utilisant cette ligne?
Swapnil B.
33

Semblable à la réponse acceptée ici, j'ai fini par devoir exclure toutes les classes liées à SSL:

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

L'ajout de cela au sommet de ma classe a résolu l'erreur.

Jason D
la source
5
Encore fallait-il ajouter quelques chemins mais tu m'as sauvé la vie! @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})
Francisco López-Sancho
Bon à savoir aussi sur com.sun.
Jason D
1
J'avais besoin de ce qui suit: @PowerMockIgnore ({"javax.management. *", "Javax.crypto. *"})
Kristof Neirynck
2
Celui-ci m'a sauvé: @PowerMockIgnore ({"javax.management. *", "Org.apache.http. *", "Com.amazonaws.http.conn.ssl. *", "Javax.net.ssl. *" , "com.sun. *", "javax.xml. *", "javax.crypto. *"})
Fayaz Ahmed
26

ClassloaderConflit de , utilisez ceci:@PowerMockIgnore("javax.management.*")

Ne laissez pas le chargeur de classe factice se charger. javax.*. Cela fonctionne.

烬 哥哥
la source
Après utilisation @PowerMockIgnore("javax.management.*"), la classe de test fonctionne bien séparément. Mais courir comme Junit testsur ce paquet a obtenu une Failed to load ApplicationContexterreur. org.apache.catalina.LifecycleException: A child container failed during startetc.
niaomingjian
8

Cela peut être un sujet un peu ancien, mais j'ai également rencontré ce problème. Il s'avère que certaines des versions java ne peuvent pas gérer powermockito lorsque powermock découvre qu'il y a 2 classes avec le même nom dans le même package (sur différentes dépendances).

Avec toute version supérieure à Java 7_25, cette erreur est générée.

Rens Groenveld
la source
2
"Avec toute version supérieure à Java 7_25, cela donne cette erreur.", Ceci est informatif.
Kajal Sinha
Qu'est-ce que cela signifie: "ne peut pas gérer powermockito"? Existe-t-il un moyen de le gérer en plus d'ignorer par annotation?
Ligne
Cela fait longtemps, mais je pense que nous avons réglé le problème en nous assurant qu'il n'y a pas 2 classes avec le même nom dans le même type de paquet. Bien sûr, si vous avez 2 bibliothèques sur lesquelles vous dépendez et qu'elles y résident ... cela va être difficile. Je ne sais pas si ce problème a été résolu entre-temps.
Rens Groenveld
3

Afin de simuler des classes système, préparez la classe qui est la cible du test, pas Thread.class. Il n'y a aucun moyen que PowerMock puisse instrumenterThread.class car il est nécessaire au démarrage de la JVM - bien avant que PowerMock puisse instrumenter.

La façon dont fonctionne l'instrumentation, une fois qu'une classe est chargée, elle ne peut plus être instrumentée.

Voir le wiki PowerMock .

cendre
la source
3

Dans PowerMock 1.7.0, une configuration globale définie par l'utilisateur peut être ajoutée au chemin de classe de votre projet. PowerMockConfig

org/powermock/extensions/configuration.properties

Ajoutez simplement une ligne dans le fichier de propriétés comme:

powermock.global-ignore=javax.management.*

Cela résoudra l'erreur pour toutes les classes de test de votre projet.

user3474985
la source