Mockito + PowerMock LinkageError podczas mockowania klasy systemu

166

Mam taki fragment kodu:

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

Wykonując te testy otrzymałem:

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)

Czy wiesz, jak mogę temu zapobiec? Być może jest inny sposób, aby kpić z takiego fragmentu kodu:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Wojciech Reszelewski
źródło
Z czego próbujesz kpić? I dlaczego?
NilsH
Pierwszy test to test pobierający i ustawiający, w tym miejscu wywołuję konstruktor (i tam występuje wyjątek). Drugi to test konstruktora. Chcę mieć kontrolę nad tym, jakie wyliczenie zasobów zawiera trzeci fragment kodu.
Wojciech Reszelewski
1
Przede wszystkim wydaje mi się, że twoje testy są bardzo ściśle powiązane z twoją implementacją. Z doświadczenia prowadzi to do kruchych testów. Najlepiej, gdy piszesz testy, myśląc o „czarnej skrzynce”. „Co ten fragment kodu ma robić” zamiast „Jak ten fragment kodu to robi”. Po drugie, myślę, że lepiej byłoby po prostu utworzyć zestaw zasobów i pozwolić środowisku wykonawczemu Javy zajmować się samym ładowaniem klas.
NilsH
Możliwe jest tworzenie różnych zestawów zasobów, ponieważ są to przypadki testowe?
Wojciech Reszelewski
Pewnie. Najłatwiej jest prawdopodobnie sparametryzować nazwy zasobów. Następnie możesz przekazać do testów różne nazwy zasobów.
NilsH

Odpowiedzi:

408

Spróbuj dodać tę adnotację do swojej klasy testowej:

@PowerMockIgnore("javax.management.*")

Pracował dla mnie.

crandrad
źródło
2
precyzja * „do Twojej klasy testowej”. Prosta i przydatna odpowiedź!
pdem
3
Czy można to również zrobić za pomocą kodu lub konfiguracji? Nie mogłem znaleźć żadnego sposobu, aby to zrobić. Mamy setki testów ... nie mogę ich wszystkich dostosować.
Frederic Leitenberger
1
@FredericLeitenberger zobacz moją odpowiedź poniżej
user3474985
2
Czy możesz również wyjaśnić intencję i znaczenie tej poprawki? Jakie instrukcje dajemy PowerMockito używając tej linii?
Swapnil B.
33

Podobnie jak w przypadku zaakceptowanej odpowiedzi tutaj, musiałem wykluczyć wszystkie klasy związane z SSL:

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

Dodanie tego na początek mojej klasy rozwiązało błąd.

Jason D.
źródło
5
Nadal potrzebowałem dodać więcej ścieżek, ale uratowałeś mi życie! @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})
Francisco López-Sancho
Dobrze wiedzieć też o com.sun.
Jason D
1
Potrzebowałem: @PowerMockIgnore ({"javax.management. *", "Javax.crypto. *"})
Kristof Neirynck
2
Ten mnie uratował: @PowerMockIgnore ({"javax.management. *", "Org.apache.http. *", "Com.amazonaws.http.conn.ssl. *", "Javax.net.ssl. *" , "com.sun. *", "javax.xml. *", "javax.crypto. *"})
Fayaz Ahmed
26

Konflikt programu ładującego klasy , użyj tego:@PowerMockIgnore("javax.management.*")

Niech mock classloader nie ładuje się javax.*. . Działa.

烬 哥哥
źródło
Po użyciu @PowerMockIgnore("javax.management.*")klasa testowa działa dobrze pojedynczo. Ale działa jak Junit testna tym pakiecie dostaje Failed to load ApplicationContextbłąd. org.apache.catalina.LifecycleException: A child container failed during starti tak dalej.
niaomingjian
8

To może być trochę stary temat, ale również napotkałem ten problem. Okazuje się, że niektóre wersje Java nie obsługują funkcji powermockito, gdy powermock stwierdzi, że istnieją 2 klasy o tej samej nazwie w tym samym pakiecie (z różnymi zależnościami).

W przypadku dowolnej wersji wyższej niż Java 7_25 występuje ten błąd.

Rens Groenveld
źródło
2
„Każda wersja wyższa niż Java 7_25 powoduje ten błąd.”, To ma charakter informacyjny.
Kajal Sinha
Co to znaczy: „nie radzę sobie z powermockito”? Czy jest jakiś sposób, aby sobie z tym poradzić, oprócz ignorowania przez adnotacje?
Linia
To było dawno temu, ale myślę, że rozwiązaliśmy to, upewniając się, że nie ma 2 klas o tej samej nazwie w tym samym rodzaju pakietu. Oczywiście, jeśli masz 2 biblioteki, na których polegasz, a one tam mieszkają ... to będzie trudne. Nie wiem, czy ten problem został w międzyczasie rozwiązany.
Rens Groenveld
3

Aby mockować klasy systemowe, należy przygotować klasę, która jest celem testu, a nie Thread.class. Nie ma możliwości, aby PowerMock był w stanie instrumentować, Thread.classponieważ jest to wymagane podczas uruchamiania JVM - na długo przed włączeniem PowerMock.

Sposób, w jaki działa instrumentacja, po załadowaniu klasy nie może już być wprowadzany.

Zobacz wiki PowerMock .

popiół
źródło
3

W PowerMock 1.7.0 globalną konfigurację zdefiniowaną przez użytkownika można dodać do ścieżki klas projektu. PowerMockConfig

org/powermock/extensions/configuration.properties

Po prostu dodaj wiersz w pliku właściwości, na przykład:

powermock.global-ignore=javax.management.*

Spowoduje to rozwiązanie błędu dla wszystkich klas testowych w projekcie.

user3474985
źródło