Reputation: 379
When I run the following code:
public class ActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
....
public void testCanCreateMockito() {
List mockedList = Mockito.mock(List.class);
}
}
I get the following exceptions:
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
This occurs with any class, List was just an easy example. My gradle dependencies are:
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')
}
I've upgraded gradle to 1.1, tried using the experimental unit test feature and not, nothing seems to make a difference. What's going on?
Upvotes: 35
Views: 16289
Reputation: 908
I stumbled upon the same error and could make it work by using
androidTestImplementation 'org.mockito:mockito-android:2.18.3'
in my build.gradle.
I previously made the mistake to only import 'org.mockito:mockito-core:2.18.3'
.
Upvotes: 2
Reputation: 5555
LOOK HERE IF YOU DON'T BUILD YOUR APKS USING GRADLE!!!!
If you don't build your app using gradle (which, unfortunately, my team doesn't) then the above solutions may not work for you. Let me explain a little bit more about how dexmaker-mockito works before giving the solution to the issue.
Motivation
Mockito is a mocking framework for Java and comes packaged with cglib which creates Bytecode mocks, this is how Mockito/Junit outside of Instrumentation tests works. But if you're trying to run Mockito in Android instrumentation tests then Bytecode mocks are not sufficient and you need mocks that Mockito can load into the Dex classloader that ART/Dalvik can understand and that's where Dexmaker comes in. Dexmaker has a Mockito "plugin" that allows Mockito to dynamically switch to using it to create Dex mocks.
How does it know to switch?
The dexmaker-mockito jar has a top level folder named mockito-extensions/org.mockito.plugins.MockMaker
containing a fully qualified name com.google.dexmaker.mockito.DexmakerMockMaker
. When this jar is packaged with an APK, this top level folder can be included as a "class loader resource".
The class that Mockito uses to abstract the Mocking layer is called MockUtil and, statically, it determines which MockMaker subclass it should use by checking for these resources in the classloader that it is launched from via its PluginLoader. If the resource from dexmaker-mockito can be found, then com.google.dexmaker.mockito.DexmakerMockMaker
is instantiated and used as the MockMaker but, if it's not, Mockito defaults to using CGLib which is incompatible within Android's DVM.
Problem
Depending on how you build your APK, you can potentially strip that class loader resource and cause Mockito to not dynamically switch to using Dexmaker.
Fix
First, include the jars that dexmaker requires: mockito 1.9.5+, junit, dexmaker-mockito 1.0+ and dexmaker 1.0+ and then simply reflectively switch which MockMaker Mockito will use manually. This is safe, given the fact that if this is running in the DVM, using the default CGLib MockMaker won't ever work since it produces 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");
}
}
}
and be sure to add this as well because dexmaker will need to know where to output its Dex mocks.
System.setProperty("dexmaker.dexcache", "/sdcard/");
Upvotes: 2
Reputation: 480
For me this eventually worked:
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"
Upvotes: 10
Reputation: 9710
User23's answer is close to the solution that worked for me.
According to the official documentation you have to do the following things to activate Mockito in your Android app:
Add the following dependencies into your 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'])
Upvotes: 1
Reputation: 2654
I was getting this because I was using proguard for my debug builds because of the 65K method limit (Yes, I need to cut down on the number of dependencies), and that was causing this error for me.
I added this in my (debug) proguard config to solve it:
### Keep Mockito
-keep class org.mockito.** { *; }
-keep interface org.mockito.** { *; }
-keep class com.google.dexmaker.** { *; }
-keep interface com.google.dexmaker.** { *; }
Not sure If I really need all four of those lines, but this did the trick.
Upvotes: 3
Reputation: 951
I received this error when I was missing the two dexmaker dependencies.
Adding these lines to the app/gradle.build file is working for me.
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
I am also using Android Studio and have found it to be a good idea to restart AS after altering the dependencies.
Upvotes: 95
Reputation: 379
I encountered the same error with EasyMock and eventually traced it to an absence of dexmaker. I solved it with the following dependency:
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
That might work for mockito as well
Upvotes: 1