Использование 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());
    }
}