Android 尝试mockito mock任何类生成ExceptionInInitializeError
当我运行以下代码时:Android 尝试mockito mock任何类生成ExceptionInInitializeError,android,unit-testing,mockito,Android,Unit Testing,Mockito,当我运行以下代码时: public class ActivityTest extends ActivityInstrumentationTestCase2<MainActivity> { .... public void testCanCreateMockito() { List mockedList = Mockito.mock(List.class); } } 我已经将gradle升级到1.1,尝试使用实验单元测试功能,但没有,似乎没有什
public class ActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
....
public void testCanCreateMockito() {
List mockedList = Mockito.mock(List.class);
}
}
我已经将gradle升级到1.1,尝试使用实验单元测试功能,但没有,似乎没有什么不同。发生了什么事?我在EasyMock中遇到了同样的错误,并最终将其归咎于缺少dexmaker。我通过以下依赖项解决了此问题:
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
这可能也适用于mockito我在缺少两个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,并且发现在更改依赖项后重新启动是一个好主意。我之所以得到这个结果,是因为我使用proguard进行调试构建,因为有65K方法限制(是的,我需要减少依赖项的数量),这对我造成了这个错误 我在我的(调试)proguard配置中添加了以下内容以解决此问题:
### Keep Mockito
-keep class org.mockito.** { *; }
-keep interface org.mockito.** { *; }
-keep class com.google.dexmaker.** { *; }
-keep interface com.google.dexmaker.** { *; }
不确定我是否真的需要这四行代码,但这就成功了。User23的答案接近于对我有效的解决方案 根据计划,您必须执行以下操作才能在Android应用程序中激活Mockito:
androidTestCompile“org.mockito:mockito核心:1.10.19”
androidtestcompilefiletree(dir:'libs',包括:['dexmaker-1.4.jar'])
androidtestcompilefiletree(dir:'libs',包括:['dexmaker-mockito-1.4.jar'])
对我来说,这最终奏效了:
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"
如果不使用GRADLE构建APK,请查看此处强>
如果您不使用gradle构建应用程序(不幸的是,我的团队没有),那么上述解决方案可能不适用于您。在给出问题的解决方案之前,让我再解释一下dexmaker mockito是如何工作的
动机
Mockito是一个针对Java的模拟框架,它与cglib一起打包,cglib创建字节码模拟,这就是Mockito/Junit在工具测试之外的工作方式。但是如果您试图在Android插装测试中运行Mockito,那么字节码mock是不够的,您需要Mockito可以加载到Dex类加载器中的mock,ART/Dalvik可以理解,这就是Dexmaker的用武之地。Dexmaker有一个Mockito“插件”,允许Mockito动态切换到使用它来创建Dex mock
System.setProperty("dexmaker.dexcache", "/sdcard/");
它如何知道切换?
dexmaker mockito jar有一个名为mockito extensions/org.mockito.plugins.MockMaker
的顶级文件夹,其中包含一个完全限定的名称com.google.dexmaker.mockito.DexmakerMockMaker
。当这个jar与APK打包时,这个顶级文件夹可以作为“类加载器资源”包含
Mockito用来抽象Mocking层的类被调用,并且静态地,它通过检查类加载器中的这些资源来确定应该使用哪个MockMaker子类,这些资源是通过它的类加载器启动的。如果可以找到来自dexmaker mockito的资源,则会实例化com.google.dexmaker.mockito.DexmakerMockMaker
并将其用作MockMaker,但如果没有,mockito默认使用CGLib,这在Android的DVM中是不兼容的
问题
根据您构建APK的方式,您可能会剥离该类装入器资源,并导致Mockito不会动态切换到使用Dexmaker
修复
首先,包括dexmaker需要的JAR:mockito 1.9.5+、junit、dexmaker mockito 1.0+和dexmaker 1.0+,然后简单地反射性地切换MockMaker mockito将手动使用的JAR。这是安全的,因为如果它在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");
}
}
}
我偶然发现了同样的错误,可以通过使用
androidTestImplementation'org.mockito:mockito android:2.18.3'
在我的build.gradle中
我之前犯了一个错误,只导入了org.mockito:mockito core:2.18.3'谢谢,这对我帮助很大。我附加了另一个参考作为这个解决方案:这对我来说适用于
@RunWIth(AndroidJunit4.class)
,但随后发现java.lang.NoClassDefFoundError:org.mockito.internal.util.MockCreationValidator
:(DexMaker到底是什么?它的作用是什么?请注意,在修改依赖项时,您只需单击“将项目与Gradle文件同步”。您不需要像以前那样完全重新启动。通过在类加载器级别进行干预来解决测试中的类路径问题似乎很脆弱,而且通常是不可取的,有点像试图在混合和烘焙后的一块蛋糕。即使你不使用Gradle,在你选择的系统中也应该有一个等效的构建文件操作来避免这里的修改。@Jeff Bowman这不是类路径问题。这是类加载器资源问题……dexmaker JAR绝对在类路径上,否则每次都会失败。我不是sure如果你有一个不使用gradle的非平凡项目,但使用Android SDK提供的不推荐和未记录的JAR解决一些构建问题并不有趣:-)谢谢你的解释!我有一个独特的情况,我正在跨多个类加载器测试代码,这帮助我让Mockito在“child”中工作类加载器。。。
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");
}
}
}
System.setProperty("dexmaker.dexcache", "/sdcard/");