【问题标题】:Mockito with Robolectric: "ClassCastException occurred when creating the proxy"带有 Robolectric 的 Mockito:“创建代理时发生 ClassCastException”
【发布时间】:2015-03-16 08:10:14
【问题描述】:

在将 Mockito 与 Robolectric 一起使用时,我开始在 Mockito 中遇到一个奇怪的 ClassCastException。当我不使用 Robolectric 跑步者运行相同的测试时,一切正常,没有抛出异常。

这是堆栈跟踪:

org.mockito.exceptions.base.MockitoException: 
ClassCastException occurred when creating the proxy.
You might experience classloading issues, disabling the Objenesis cache *might* help (see MockitoConfiguration)
    at com.compassrosetech.ccs.android.test.ObservableCacheDispatcherTest.setUp(ObservableCacheDispatcherTest.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.ClassCastException: kotlin.Function0$$EnhancerByMockitoWithCGLIB$$c0163e7f cannot be cast to org.mockito.cglib.proxy.Factory
    at org.mockito.internal.creation.jmock.ClassImposterizer.createProxy(ClassImposterizer.java:128)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:63)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:56)
    at org.mockito.internal.creation.CglibMockMaker.createMock(CglibMockMaker.java:23)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:51)
    at org.mockito.Mockito.mock(Mockito.java:1243)
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:30)
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16)
    at org.mockito.internal.configuration.DefaultAnnotationEngine.createMockFor(DefaultAnnotationEngine.java:43)
    at org.mockito.internal.configuration.DefaultAnnotationEngine.process(DefaultAnnotationEngine.java:66)
    at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:71)
    at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:55)
    at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:108)
    ... 29 more

我使用的代码如下:

@RunWith(ApplicationTestRunner.class)
public class ObservableCacheDispatcherTest {

    @Mock
    private IDataMapper mockDataMapper;

    @Mock
    private ICache mockCache;

    @Mock
    private Function0<Object> mockLoader;

    private ObservableCacheDispatcher cacheDispatcher;


    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this); // <-- exception happens when executing this statement
        cacheDispatcher = new ObservableCacheDispatcher(mockCache, mockDataMapper);
    }

    ...
}

这是 Robolectric 跑步者的样子:

public class ApplicationTestRunner extends RobolectricTestRunner {

    //Maximun SDK Robolectric will compile (issues with SDK > 18)
    private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC = 18;

    private static final String ANDROID_MANIFEST_PATH = "../app/src/main/AndroidManifest.xml";
    private static final String ANDROID_MANIFEST_RES_PATH = "../app/src/main/res";

    /**
     * Call this constructor to specify the location of resources and AndroidManifest.xml.
     *
     * @throws org.junit.runners.model.InitializationError
     */
    public ApplicationTestRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
    }

    @Override protected AndroidManifest getAppManifest(Config config) {
        return new AndroidManifest(Fs.fileFromPath(ANDROID_MANIFEST_PATH),
                Fs.fileFromPath(ANDROID_MANIFEST_RES_PATH)) {
            @Override
            public int getTargetSdkVersion() {
                return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC;
            }
        };
    }
}

我应该如何解决这个异常?

【问题讨论】:

  • 有趣!我只是猜测与 Kotlin/Robolectric 有冲突。 Kotlin 会取代 ClassLoader 吗?我希望 Kotlin 专家能加入讨论
  • @EugenMartynov 好点,我已经添加了“kotlin”标签,也许 Kotlin 人会有所帮助。
  • @EugenMartynov 我已经尝试过该解决方案,但没有帮助。我可能是因为我没有使用 robolectric-gradle-plugin,测试在一个单独的纯 Java 模块中。
  • Alex,很抱歉在没有很好理解问题的情况下将链接扔给您。经过一些研究,当有人使用PowerMock(自定义类加载器)(github.com/mockito/mockito/issues/98)时,我看到了类似的堆栈跟踪。进一步调查

标签: java android mockito robolectric


【解决方案1】:

按照建议

'禁用 Objenesis 缓存可能有帮助'

您可以创建覆盖 MockitoConfiguration。
这样做

  • 在您的 src/test/java 下创建一个名为 org.mockito.configuration 的包
  • 编写 MockitoConfiguration 类

类文件

package org.mockito.configuration;

public class MockitoConfiguration extends DefaultMockitoConfiguration {

  @Override
  public boolean enableClassCache() {
    return false;
  }
}

尝试再次构建您的代码。

【讨论】:

    【解决方案2】:

    我知道问题有点老了,但它可能会帮助寻找答案的人。

    添加

    @PowerMockIgnore({ "org.mockito.*"})
    

    之后

    @RunWith(ApplicationTestRunner.class)
    

    应该可以解决问题。

    【讨论】:

      【解决方案3】:

      我也遇到过类似的问题。基本上,你有一些你想要 Mockito 模拟的类和你想要 PowerMock 模拟的其他类。我通过将@PowerMockIngore 注释添加到我的测试类并指定我想使用常规'ol mockito 模拟的类来解决它。所以在你的情况下,我认为添加注释:

      @PowerMockIgnore({"com.package.IDataMapper", "com.package.ICache" ..etc})
      

      会起作用的。

      【讨论】:

        【解决方案4】:

        这实际上不是 Kotlin 问题,而是 Robolectric/Mockito。我相信它与类加载器有关。如果您将 Kotlin 类替换为任何其他库接口(既不是 JDK 也不是您的项目的类),您将看到相同的异常。不幸的是,我看不到任何解决方案,因为我不了解 Robolectric 内部结构。您最好向 Robolectric 或 Mockito 团队询问此事。

        【讨论】:

        • 我使用mockitorobolectric 没有任何问题。类似的代码对我来说很好。是robolectricmockito 的特定版本吗?
        • 似乎是。我发现问题是由测试运行器(Launcher/AsmInstrumentingClassLoader)和mockito的类加载器(SearchingClassLoader)使用的不同类加载器引起的,因此存根类是通过一个生成的,但代理类(我们试图转换为)是由另一个加载的。这需要更多调查,我很确定 Mockito 或 Robolectric 团队可以做得更好。
        • 等一下! Launcher/AsmInstrumentingClassLoader 是否来自 android gradle 插件,但不是来自 Robolectric?你使用什么版本的 Mockito?
        • 其实没关系。 Mockito 和 Robolectric 都使用类加载器,并且在不同的类加载器中有两个相同的类实例,由于 ClassCastException 导致测试失败。也许有一些解决方法,但我不知道
        • 对我来说,当我开始收到此错误时,将PowerMock 添加到图片中时会出现问题。
        【解决方案5】:

        解决方法谨慎!

        请自行承担使用风险。

        首先你必须重新定义 kotlin 函数接口:

        interface TestFunction0<R> : Function0<R>
        interface TestFunction1<T1, R> : Function1<T1, R>
        

        并模拟它:

        val onUpdate: TestFunction0<Unit> = mock()
        

        【讨论】:

        • 这甚至不编译
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-26
        相关资源
        最近更新 更多