Mockito + PowerMock LinkageError sambil mengejek kelas sistem

166

Saya memiliki cuplikan kode seperti itu:

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

Saat menjalankan tes ini saya mendapat:

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)

Apakah Anda tahu bagaimana saya bisa mencegah ini? Saya mungkin ada cara lain untuk mengejek sepotong kode:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
...
Enumeration<URL> resources = classLoader.getResources(path);
Wojciech Reszelewski
sumber
Apa yang kamu coba mengejek? Dan mengapa?
NilsH
Tes pertama adalah getter dan setter test, saya memanggil konstruktor di sana (dan di sana pengecualian terjadi). Yang kedua adalah tes konstruktor. Saya ingin mendapatkan kendali atas apa yang disebutkan oleh enumerasi sumber daya di cuplikan kode ketiga.
Wojciech Reszelewski
1
Pertama-tama, tampak bagi saya bahwa tes Anda sangat erat dengan implementasi Anda. Berdasarkan pengalaman, ini akan menyebabkan tes rapuh. Lebih disukai, Anda ingin berpikir "kotak hitam" saat menulis tes Anda. "Apa yang seharusnya dilakukan oleh kode ini", daripada "Bagaimana kode ini dapat dilakukan". Kedua, saya pikir Anda akan lebih baik hanya membuat satu set sumber daya dan membiarkan Java runtime menangani classloading itu sendiri.
NilsH
Dimungkinkan untuk membuat berbagai set sumber daya, karena mereka di mana menguji kasus?
Wojciech Reszelewski
Tentu. Yang termudah bagi Anda mungkin adalah untuk menentukan nama sumber daya. Kemudian Anda bisa memasukkan nama sumber daya yang berbeda ke dalam tes Anda.
NilsH

Jawaban:

408

Coba tambahkan anotasi ini ke kelas Tes Anda:

@PowerMockIgnore("javax.management.*")

Bekerja untukku.

Crandrad
sumber
2
presisi * "ke Kelas Tes Anda". Jawaban sederhana dan bermanfaat!
pdem
3
Apakah ini dapat dilakukan dengan kode atau konfigurasi juga? Saya tidak dapat menemukan cara untuk melakukan ini. Kami memiliki ratusan tes ... saya tidak dapat menyesuaikan semuanya.
Frederic Leitenberger
1
@FredericLeitenberger lihat jawaban saya di bawah ini
user3474985
2
Bisakah Anda juga menjelaskan intution dan arti dari perbaikan ini? Instruksi apa yang kami berikan kepada PowerMockito menggunakan baris itu?
Swapnil B.
33

Mirip dengan respons yang diterima di sini, saya akhirnya harus mengecualikan semua kelas terkait SSL:

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

Menambahkan itu ke atas kelas saya menyelesaikan kesalahan.

Jason D
sumber
5
Masih perlu menambahkan beberapa jalur lagi tetapi Anda menyelamatkan hidup saya, manusia! @PowerMockIgnore({"javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.*", "javax.net.ssl.*","com.sun.*"})
Francisco López-Sancho
Senang mengetahui tentang com.sun juga.
Jason D
1
Saya membutuhkan yang berikut: @PowerMockIgnore ({"javax.management. *", "Javax.crypto. *"})
Kristof Neirynck
2
Yang ini menyelamatkan saya: @PowerMockIgnore ({"javax.management. *", "Org.apache.http. *", "Com.amazonaws.http.conn.ssl. *", "Javax.net.ssl. *" , "com.sun. *", "javax.xml. *", "javax.crypto. *"})
Fayaz Ahmed
26

Konflik Classloader , gunakan ini:@PowerMockIgnore("javax.management.*")

Biarkan mock classloader tidak memuatnya javax.*. berfungsi.

烬 哥哥
sumber
Setelah menggunakan @PowerMockIgnore("javax.management.*"), kelas tes bekerja dengan baik secara tunggal. Tetapi berjalan seperti Junit testpada paket itu mendapat Failed to load ApplicationContextkesalahan. org.apache.catalina.LifecycleException: A child container failed during startdan seterusnya.
niaomingjian
8

Ini mungkin sedikit topik lama, tapi saya juga mengalami masalah ini. Ternyata beberapa versi java tidak dapat menangani powermockito ketika powermock menemukan ada 2 kelas dengan nama yang sama dalam paket yang sama (lebih dari dependensi yang berbeda).

Dengan versi apa pun yang lebih tinggi dari Java 7_25 itu memberikan kesalahan ini.

Rens Groenveld
sumber
2
"Dengan versi apa pun yang lebih tinggi dari Java 7_25 itu memberikan kesalahan ini.", Ini informatif.
Kajal Sinha
Apa artinya: "tidak dapat menangani powermockito"? Apakah ada cara untuk menghadapinya selain mengabaikan dengan anotasi?
Baris
Sudah lama sekali, tapi saya pikir kami mengatasinya dengan memastikan tidak ada 2 kelas dengan nama yang sama dalam jenis paket yang sama. Tentu saja, jika Anda memiliki 2 perpustakaan yang Anda andalkan, dan mereka tinggal di sana ... itu akan sulit. Saya tidak tahu apakah masalah ini telah diperbaiki untuk sementara waktu.
Rens Groenveld
3

Untuk mengejek kelas sistem, persiapkan kelas yang menjadi target ujian, bukan Thread.class. Tidak mungkin PowerMock akan dapat instrumen Thread.classkarena diperlukan selama startup JVM - jauh sebelum PowerMock dapat instrumen.

Cara kerja instrumentasi, begitu kelas dimuat, itu tidak bisa lagi diintrumentasikan.

Lihat wiki PowerMock .

Abu
sumber
3

Di PowerMock 1.7.0 konfigurasi global yang ditentukan pengguna dapat ditambahkan ke classpath proyek Anda. PowerMockConfig

org/powermock/extensions/configuration.properties

Cukup tambahkan baris dalam file properti seperti:

powermock.global-ignore=javax.management.*

Ini akan menyelesaikan kesalahan untuk semua kelas tes di proyek Anda.

pengguna3474985
sumber