【问题标题】:Using PowerMock and Robolectric - IncompatibleClassChangeError使用 PowerMock 和 Robolectric - IncompatibleClassChangeError
【发布时间】:2013-12-31 16:57:10
【问题描述】:

我正在尝试使用 PowerMockito 在 Android Robolectric 测试中模拟一些静态方法。我按照here 的指示使用 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
在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)
在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 在 java.lang.reflect.Method.invoke(Method.java:597)
在 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: class 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(Native Method)

每个单元测试中。

根据 Maven depency:tree 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());
    }
}

【问题讨论】:

  • 要使用 power mock,您必须使用 power mock runner 运行它。因此,当您尝试使用 Robolectric 测试运行器运行它时,它没有意义
  • @EugenMartynov 这就是 javaagent 和 PowerMockRule 应该解决的问题,请参阅 PowerMockRulePowerMockAgent
  • 酷,如果你解决了,请告诉我,因为这也是我们的问题
  • @EugenMartynov 如果我弄明白了,我会在这里发布。
  • @EugenMartynov 有another StackOverflow questionIssue Logged。看来是来源有问题。

标签: java android powermock robolectric


【解决方案1】:

我找到了一种将 PowerMock 与 Robolectric 结合使用的方法。

除了标准的 PowerMock jar,还需要 PowerMock Junit Rule。它描述了here如何抓取它。我使用了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());
    }
}

【讨论】:

  • 我曾使用过一些这种方法,它已发布在相关的 Android 问题中,并且我遇到了 Robolectric 类加载器和 Powermock 类加载器之间的冲突。
  • 请注意,Robolectric 可能无法正确加载到阴影中,这是我目前在使用此设置时遇到的问题
猜你喜欢
  • 2012-11-30
  • 2016-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-09
  • 2016-04-14
  • 1970-01-01
相关资源
最近更新 更多