Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
211 views
in Technique[技术] by (71.8m points)

java - Using PowerMock and Robolectric - IncompatibleClassChangeError

I'm trying to use PowerMockito to mock some static methods in Android Robolectric tests. I'm using JUnit 4.8.2, Robolectric 2.2, Mockito 1.9.5, and PowerMock 1.9.5 as directed here. As I have to use the RoboElectricTestRunner, I'm attempting to use the PowerMockRule to bootstrap PowerMock. However I'm getting an unfortunate java.lang.IncompatibleClassChangeError when the test with PowerMock runs.

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:323) at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:348)

Caused by: java.lang.IncompatibleClassChangeError: Implementing class at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:637) at java.lang.ClassLoader.defineClass(ClassLoader.java:621) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)

If I put org.ow2.asm after the org.powermock libraries I get:

java.lang.IncompatibleClassChangeError: class org.objectweb.asm.tree.ClassNode has interface org.objectweb.asm.ClassVisitor as super class at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:637) at java.lang.ClassLoader.defineClass(ClassLoader.java:621) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) at java.net.URLClassLoader.access$000(URLClassLoader.java:58) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method)

on every unit test.

According to Maven depency:tree Robolectric and PowerMock don't share any dependencies. But apparently org.powermock:powermock-module-javaagent packages some org/objectweb/asm classes, and Robolectric relies on org.ow2.asm:asm:jar:4.1 causing a conflict.


@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());
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I found a way to use PowerMock in conjunction with Robolectric.

In addition to the standard PowerMock jars, the PowerMock Junit Rule is also needed. It is described here how to grab it. I used the xstream classloading version, because the objenesis one is very buggy. This is working with PowerMock 1.5.5 and Robolectric 2.3, i cannot speak about the older versions. Also please note that the Java agent should not be included, because from my experience it causes problems.

So if you are using maven, these dependencies should be declared:

<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>

Then you have to setup your test class like this:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...