【问题标题】:Attempt to mockito mock any class generates ExceptionInInitializerError尝试模拟任何类都会生成 ExceptionInInitializerError
【发布时间】:2015-05-31 04:44:22
【问题描述】:

当我运行以下代码时:

public class ActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
    ....
    public void testCanCreateMockito() {
        List mockedList = Mockito.mock(List.class);
    }
}

我得到以下异常:

java.lang.ExceptionInInitializerError
at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at com.acesounderglass.hungertracker.ActivityTest.testCanCreateMockito(ActivityTest.java:60)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1837)
Caused by: org.mockito.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:238)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)
... 23 more
Caused by: java.lang.reflect.InvocationTargetException
at org.mockito.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:385)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:220)
... 28 more
Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
at java.lang.ClassLoader.defineClass(ClassLoader.java:300)
... 32 more

这发生在任何类中,List 只是一个简单的例子。我的 gradle 依赖项是:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.0'
    androidTestCompile "org.mockito:mockito-core:1.+"
    androidTestCompile files('libs/dexmaker-mockito-1.0.jar')
    androidTestCompile files('libs/dexmaker-1.0.jar')
}

我已将 gradle 升级到 1.1,尝试使用实验性单元测试功能,但没有,似乎没有任何区别。怎么回事?

【问题讨论】:

    标签: android unit-testing mockito


    【解决方案1】:

    我在使用 EasyMock 时遇到了同样的错误,最终追踪到没有 dexmaker。我用以下依赖解决了它:

    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
    

    这可能也适用于 mockito

    【讨论】:

      【解决方案2】:

      当我缺少两个 dexmaker 依赖项时收到此错误。

      将这些行添加到 app/gradle.build 文件对我有用。

      androidTestCompile 'org.mockito:mockito-core:1.10.19'
      androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
      androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
      

      我也在使用 Android Studio,发现更改依赖项后重启 AS 是个好主意。

      【讨论】:

      • 谢谢,这对我帮助很大。我附上了另一个参考作为这个解决方案:luisdelarosa.com/2013/04/15/…
      • 这对我有用 @RunWIth(AndroidJunit4.class) 但随后发现 java.lang.NoClassDefFoundError: org.mockito.internal.util.MockCreationValidator :(
      • DexMaker 到底是什么,它有什么作用?
      • 注意,修改依赖项时只需要点击Sync Project with Gradle Files即可。不需要完全重启 AS。
      【解决方案3】:

      我得到这个是因为我在调试版本中使用了 proguard,因为 65K 方法限制(是的,我需要减少依赖项的数量),这对我来说是这个错误。

      我在我的(调试)proguard 配置中添加了这个来解决它:

      ### Keep Mockito
      -keep class org.mockito.** { *; }
      -keep interface org.mockito.** { *; }
      -keep class com.google.dexmaker.** { *; }
      -keep interface com.google.dexmaker.** { *; }
      

      不确定我是否真的需要所有这四行,但这成功了。

      【讨论】:

        【解决方案4】:

        User23 的答案与对我有用的解决方案接近。

        根据official documentation,您必须执行以下操作才能在您的 Android 应用中激活 Mockito:

        1. 下载 dexmaker-1.4.jardexmaker-mockito-1.4.jar 并将它们放入您的 libs 文件夹中。
        2. 将以下依赖项添加到您的 build.gradle 中:

          androidTestCompile "org.mockito:mockito-core:1.10.19"
          androidTestCompile fileTree(dir: 'libs', include: ['dexmaker-1.4.jar']) androidTestCompile fileTree(dir: 'libs', include: ['dexmaker-mockito-1.4.jar'])

        【讨论】:

        • 链接官方文档的额外道具。
        【解决方案5】:

        对我来说,这最终奏效了:

        androidTestCompile "org.mockito:mockito-core:1.10.19"
        androidTestCompile "com.crittercism.dexmaker:dexmaker:1.4"
        androidTestCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4"
        androidTestCompile "com.crittercism.dexmaker:dexmaker-dx:1.4"
        

        【讨论】:

          【解决方案6】:

          如果您不使用 GRADLE 构建您的 APK,请看这里!!!!

          如果您不使用 gradle 构建您的应用程序(很遗憾,我的团队没有),那么上述解决方案可能不适合您。在给出问题的解决方案之前,让我稍微解释一下 dexmaker-mockito 的工作原理。

          动机

          Mockito 是一个用于 Java 的模拟框架,并与创建字节码模拟的 cglib 打包在一起,这就是 Mockito/Junit 在 Instrumentation 测试之外的工作方式。但是,如果您尝试在 Android 仪器测试中运行 Mockito,那么 Bytecode 模拟是不够的,您需要 Mockito 可以加载到 ART/Dalvik 可以理解的 Dex 类加载器中的模拟,这就是 Dexmaker 的用武之地。Dexmaker 有一个 Mockito“插件" 允许 Mockito 动态切换到使用它来创建 Dex 模拟。

          它是如何知道切换的?

          dexmaker-mockito jar 有一个名为 mockito-extensions/org.mockito.plugins.MockMaker 的顶级文件夹,其中包含一个完全限定名称 com.google.dexmaker.mockito.DexmakerMockMaker。当此 jar 与 APK 一起打包时,此顶级文件夹可以作为“类加载器资源”包含在内。

          Mockito 用来抽象 Mocking 层的类称为MockUtil,静态地,它通过在通过其PluginLoader 启动的类加载器中检查这些资源来确定它应该使用哪个 MockMaker 子类。如果可以找到来自 dexmaker-mockito 的资源,则将 com.google.dexmaker.mockito.DexmakerMockMaker 实例化并用作 MockMaker,但如果不是,则 Mockito 默认使用与 Android 的 DVM 不兼容的 CGLib。

          问题

          根据您构建 APK 的方式,您可能会剥离该类加载器资源并导致 Mockito 无法动态切换到使用 Dexmaker。

          修复

          首先,包含 dexmaker 需要的 jar:mockito 1.9.5+、junit、dexmaker-mockito 1.0+ 和 dexmaker 1.0+,然后简单地反射性地切换手动使用哪个 MockMaker Mockito。这是安全的,因为如果它在 DVM 中运行,使用默认的 CGLib MockMaker 将永远无法工作,因为它会生成字节码模拟。

          static {
              try {
                  ClassLoader loader = Thread.currentThread().getContextClassLoader();
                  if (loader == null) {
                      loader = ClassLoader.getSystemClassLoader();
                  }
          
                  Enumeration<URL> resources = loader.getResources("mockito-extensions/org.mockito.plugins.MockMaker");
                  if (resources == null || !resources.hasMoreElements()) {
                      LOGGER.info("Replacing Mockito mockMaker because the classloader resources were not present.");
                      Field field = MockUtil.class.getDeclaredField("mockMaker");
                      Class<?> pluginClass = loader.loadClass("com.google.dexmaker.mockito.DexmakerMockMaker");
                      Object plugin = pluginClass.newInstance();
                      field.setAccessible(true);
                      field.set(null, plugin);
                  } else {
                      LOGGER.info("Mockito class loader resources present");
                  }
              } catch (Throwable e) {
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                      throw new AssertionError("Unable to replace mockMaker to be the DexmakerMockMaker", e);
                  } else {
                      e.printStackTrace();
                      throw new AssertionError("Unable to replace mockMaker to be the DexmakerMockMaker");
                  }
              }
          }
          

          并且一定要添加这个,因为 dexmaker 需要知道在哪里输出它的 Dex 模拟。

          System.setProperty("dexmaker.dexcache", "/sdcard/");
          

          【讨论】:

          • 通过在类加载器级别进行干预来解决测试中的类路径问题似乎很脆弱,通常是不可取的,这有点像在混合和烘烤后试图从蛋糕中取出蛋黄。即使您不使用 Gradle,也应该在您选择的系统中进行等效的构建文件操作,以避免此处进行修改。
          • @Jeff Bowman 这不是类路径问题。这是一个类加载器资源问题...... dexmaker jar 绝对在类路径上,否则每次都会失败。我不确定您是否有一个不使用 gradle 的重要项目,但使用 Android SDK 提供的已弃用和未记录的 jar 解决一些构建问题并不有趣:-)
          • 感谢您的解释!我有一个独特的情况,我正在跨多个类加载器测试代码,这帮助我让 Mockito 在“子”类加载器中工作......
          【解决方案7】:

          我偶然发现了同样的错误,并且可以通过使用使其工作

          androidTestImplementation 'org.mockito:mockito-android:2.18.3'

          在我的 build.gradle 中。

          我之前犯的错误是只导入'org.mockito:mockito-core:2.18.3'

          【讨论】:

            猜你喜欢
            • 2021-07-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-01
            • 2012-07-31
            • 2021-09-17
            • 1970-01-01
            相关资源
            最近更新 更多