尝试mockito模拟任何类生成ExceptionInInitializerError

时间:2015-03-26 23:34:41

标签: android unit-testing mockito

当我运行以下代码时:

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,尝试使用实验单元测试功能而不是,似乎没有任何区别。发生了什么事?

7 个答案:

答案 0 :(得分:89)

当我错过了两个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是一个好主意。

答案 1 :(得分:9)

对我而言,这最终奏效了:

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"

答案 2 :(得分:3)

我得到这个是因为我使用proguard进行调试构建,因为65K方法限制(是的,我需要减少依赖项的数量),这对我造成了这个错误。

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

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

不确定如果我真的需要所有这四条线,但这就行了。

答案 3 :(得分:1)

我遇到了与EasyMock相同的错误,并最终将其追溯到缺少dexmaker。我用以下依赖项解决了它:

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

这可能也适用于mockito

答案 4 :(得分:1)

如果你没有使用年级建立你的APK!

如果您不使用gradle构建应用程序(不幸的是,我的团队没有),那么上述解决方案可能对您不起作用。在解决问题之前,让我先解释一下dexmaker-mockito的工作原理。

<强>动机

Mockito是Java的一个模拟框架,它与cglib一起打包创建了Bytecode模拟,这就是Instrumentation测试之外的Mockito / Junit的工作原理。但是如果你试图在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,并且静态地,它通过检查类加载器中通过其{{3}启动的这些资源来确定它应该使用哪个MockMaker子类。 }}。如果可以找到来自dexmaker-mockito的资源,那么com.google.dexmaker.mockito.DexmakerMockMaker将被实例化并用作MockMaker,但如果不是,则Mockito默认使用CGLib,这在Android的DVM中是不兼容的。

<强>问题

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

<强>修正

首先,包括dexmaker要求的罐子:mockito 1.9.5 +,junit,dexmaker-mockito 1.0+和dexmaker 1.0+,然后简单地反射切换哪个MockMaker Mockito将手动使用。这是安全的,因为如果它在DVM中运行,使用默认的CGLib MockMaker将无法工作,因为它会产生Bytecode模拟。

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/");

PluginLoader

答案 5 :(得分:1)

我偶然发现了相同的错误,可以通过使用

使其正常工作

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

在我的 build.gradle 中。

我以前犯过错误,仅导入'org.mockito:mockito-core:2.18.3'

答案 6 :(得分:0)

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'])