web-dev-qa-db-ja.com

クラスをモックモックしようとすると、ExceptionInInitializerErrorが生成されます

次のコードを実行すると:

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

これはどのクラスでも発生します。リストは簡単な例です。私の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にアップグレードし、実験的な単体テスト機能を使用してみましたが、何も違いはありません。どうしたの?

31

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を再起動することをお勧めします。

90
user23

私にとってこれは最終的に機能しました:

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

これは、65Kメソッドの制限のためにデバッグビルドにproguardを使用していたためです(はい、依存関係の数を減らす必要があります)。それがこのエラーの原因でした。

私はそれを解決するために私の(デバッグ)プロガード構成にこれを追加しました:

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

これらの4行すべてが本当に必要かどうかはわかりませんが、これでうまくいきました。

3
Ciske Boekelo

GRADLEを使用してAPKを構築しない場合はこちらをご覧ください!!!!

Gradleを使用してアプリを構築しない場合(残念ながら、私のチームはそうしません)、上記のソリューションはうまくいかないかもしれません。この問題の解決策を示す前に、dexmaker-mockitoがどのように機能するかについてもう少し説明します。

動機

MockitoはJavaのモックフレームワークであり、バイトコードモックを作成するcglibにパッケージ化されています。これが、インストルメンテーションテスト以外でのMockito/Junitの動作です。しかし、AndroidインスツルメンテーションテストでMockitoを実行しようとしている場合、Bytecodeモックでは不十分であり、MockitoがART/Dalvikが理解できるDexクラスローダーにロードできるモックが必要です。 Dexmakerには、Mockitoを使用して動的に切り替えてDexモックを作成できるようにするMockito「プラグイン」があります。

どのように切り替えることがわかりますか?

Dexmaker-mockito jarには、mockito-extensions/org.mockito.plugins.MockMakerという名前の最上位フォルダーがあり、完全修飾名com.google.dexmaker.mockito.DexmakerMockMakerが含まれています。このjarがAPKでパッケージ化されている場合、この最上位フォルダーを「クラスローダーリソース」として含めることができます。

MockitoがMockingレイヤーを抽象化するために使用するクラスは MockUtil と呼ばれ、静的に、クラスローダーでこれらのリソースをチェックして、使用するMockMakerサブクラスを決定します PluginLoader 。 dexmaker-mockitoのリソースが見つかった場合、com.google.dexmaker.mockito.DexmakerMockMakerがインスタンス化されてMockMakerとして使用されますが、見つからない場合、MockitoはAndroidのDVM内で互換性のないCGLibを使用します。

問題

APKの構築方法によっては、そのクラスローダーリソースを削除し、Mockitoが動的にDexmakerの使用に切り替わらないようにすることができます。

修正

まず、dexmakerが必要とするjarファイルを含めます。mockito1.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/");

mockito-extensions/org.mockito.plugins.MockMaker

EasyMockでも同じエラーが発生し、最終的にはdexmakerが存在しないことを突き止めました。私は次の依存関係でそれを解決しました:

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

それはmockitoでもうまくいくかもしれません

1

User23の答えは、私にとってうまくいった解決策に近いものです。

公式ドキュメント によると、AndroidアプリでMockitoを有効にするには次のことを行う必要があります。

  1. dexmaker-1.4.jar および dexmaker-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'])

0
Alexander Pacha