Использование PowerMock и Robolectric - IncompatibleClassChangeError
Я пытаюсь использовать PowerMockito для моделирования некоторых статических методов в тестах Android Robolectric. Я использую JUnit 4.8.2, Robolectric 2.2, Mockito 1.9.5 и PowerMock 1.9.5 в соответствии с здесь. Поскольку я должен использовать RoboElectricTestRunner, я пытаюсь использовать PowerMockRule для загрузки PowerMock. Однако при запуске теста с PowerMock я получаю неудачный java.lang.IncompatibleClassChangeError
.
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Нативный метод)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) на sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) в java.lang.reflect.Method.invoke(Method.java:597)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:323) на sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:348)
Вызвано: java.lang.IncompatibleClassChangeError: реализация класса в java.lang.ClassLoader.defineClass1 (Нативный метод)
в java.lang.ClassLoader.defineClassCond(ClassLoader.java:637) в java.lang.ClassLoader.defineClass(ClassLoader.java:621) в java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
Если я поместил org.ow2.asm
после библиотек org.powermock
, я получаю:
java.lang.IncompatibleClassChangeError: класс org.objectweb.asm.tree.ClassNode имеет интерфейс org.objectweb.asm.ClassVisitor как суперкласс в java.lang.ClassLoader.defineClass1 (собственный метод) в java.lang.ClassLoader.defineClassCond(ClassLoader.java:637) в java.lang.ClassLoader.defineClass(ClassLoader.java:621) в java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) на java.net.URLClassLoader.defineClass(URLClassLoader.java:283) на java.net.URLClassLoader.access $000 (URLClassLoader.java:58) на java.net.URLClassLoader $1.run(URLClassLoader.java:197) в java.security.AccessController.doPrivileged(собственный метод)
на каждом unit test.
Согласно описанию Maven: дерево Robolectric и PowerMock не разделяют никаких зависимостей. Но явно org.powermock: powermock-module-javaagent пакеты некоторых классов org/objectweb/asm, а Robolectric полагается на org. ow2.asm: asm: jar: 4.1 вызывает конфликт.
@RunWith(RobolectricTestRunner.class)
@PrepareForTest(Helper.class)
@PowerMockIgnore({"com.sun.jmx.*", "javax.management.*"})
public class HelpFragTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
static {
PowerMockAgent.initializeIfNeeded();
}
FragmentActivity fragmentActivity;
FragmentManager fragmentManager;
ActionBarManager actionBarManager;
@Before
public void setup(){
actionBarManager = mock(ActionBarManager.class);
LowesApplication.instance().setActionBarManager(actionBarManager);
fragmentActivity = Robolectric.buildActivity(FragmentActivity.class).create().start().resume().get();
fragmentManager = fragmentActivity.getSupportFragmentManager();
}
@Test
public void testShow(){
mockStatic(Helper.class);
HelpFrag helpFrag = HelpFrag.newInstance();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(helpFrag, null);
fragmentTransaction.commit();
assertTrue(helpFrag.isVisible());
}
}
Ответы
Ответ 1
Я нашел способ использовать PowerMock в сочетании с Robolectric.
В дополнение к стандартным банкам PowerMock также требуется правило PowerMock Junit. Здесь описывается , как его схватить. Я использовал версию загрузки xstream
, потому что objenesis
один очень плохой. Это работает с PowerMock 1.5.5 и Robolectric 2.3, я не могу говорить о более старых версиях. Также обратите внимание, что агент Java не должен включаться, потому что по моему опыту он вызывает проблемы.
Итак, если вы используете maven, эти зависимости должны быть объявлены:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-classloading-xstream</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
Затем вам нужно настроить свой тестовый класс следующим образом:
@RunWith(RobolectricTestRunner.class)
@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
@PrepareForTest(Static.class)
public class MyTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
private MyActivity activity;
@Before
public void setup() {
activity = Robolectric.buildActivity(MyActivity.class).create().get();
}
@Test
public void test() throws Exception {
PowerMockito.mockStatic(Static.class);
Mockito.when(Static.getCurrentTime()).thenReturn(1);
Assert.assertEquals(1, activity.getId());
}
}