Подтвердить что ты не робот

Попытка mockito издеваться над любым классом генерирует 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

Это происходит с любым классом, 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, попробовал использовать экспериментальную функцию unit test, а не, ничего, похоже, не имеет значения. Что происходит?

4b9b3361

Ответ 1

Я получил эту ошибку, когда мне не хватало две зависимости 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 после изменения зависимостей.

Ответ 2

Для меня это в конечном итоге сработало:

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"

Ответ 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.** { *; }

Не уверен. Если мне действительно нужны все четыре из этих строк, но это сделало трюк.

Ответ 4

Я столкнулся с той же ошибкой с EasyMock и в конечном итоге проследил ее до отсутствия dexmaker. Я решил это со следующей зависимостью:

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

Это может работать и для mockito

Ответ 5

СМОТРИТЕ ЗДЕСЬ, ЕСЛИ ВЫ НЕ СОЗДАВАЕТЕ ВАШИ APKS ИСПОЛЬЗУЕТСЯ GRADLE!!!!

Если вы не создаете свое приложение с помощью gradle (к сожалению, у моей команды нет), то вышеупомянутые решения могут не сработать для вас. Позвольте мне объяснить немного больше о том, как работает dexmaker-mockito, прежде чем дать решение проблемы.

Мотивация

Mockito - это издевательская структура для Java и поставляется в комплекте с cglib, который создает Bytecode mocks, так работает Mockito/Junit вне тестов Instrumentation. Но если вы пытаетесь запустить Mockito в тестах на аппаратные средства Android, тогда макеты Bytecode недостаточно, и вам понадобятся макеты, которые Mockito может загружать в загрузчик классов Dex, который может понять ART/Dalvik, и то, где приходит Dexmaker. Dexmaker имеет плагин Mockito ", что позволяет Mockito динамически переключаться на его использование для создания Dex mocks.

Как это знать для переключения?

В банке dexmaker-mockito папка верхнего уровня с именем mockito-extensions/org.mockito.plugins.MockMaker содержит полное имя com.google.dexmaker.mockito.DexmakerMockMaker. Когда эта банка упакована в APK, эта папка верхнего уровня может быть включена как "ресурс загрузчика класса".

Класс, который Mockito использует для абстрагирования слоя Mocking, называется MockUtil и статически определяет, какой подкласс MockMaker он должен использовать, проверяя для этих ресурсов в загрузчике классов, который запускается с помощью PluginLoader. Если ресурс dexmaker-mockito можно найти, то com.google.dexmaker.mockito.DexmakerMockMaker создается и используется как MockMaker, но если это не так, Mockito по умолчанию использует CGLib, который несовместим с Android DVM.

Проблема

В зависимости от того, как вы создаете APK, вы можете лишить ресурс загрузчика этого класса и заставить Mockito не динамически переключаться на использование Dexmaker.

Fix

Во-первых, включите банки, для которых требуется dexmaker: mockito 1.9.5+, junit, dexmaker-mockito 1.0+ и dexmaker 1.0+, а затем просто рефлексивно переключите, какой MockMaker Mockito будет использовать вручную. Это безопасно, учитывая тот факт, что если это работает в DVM, использование CGLib MockMaker по умолчанию никогда не будет работать, так как он создает Bytecode mocks.

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 mocks.

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

mockito-extensions/org.mockito.plugins.MockMaker

Ответ 6

Ответ User23 близок к решению, которое сработало для меня.

В соответствии с официальной документацией вам необходимо выполнить следующие действия для активации Mockito в вашем приложении для Android:

  • Загрузите dexmaker-1.4.jar и dexmaker-mockito-1.4.jar и поместите их в папку libs.
  • Добавьте в свой файл 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'])