使用 Roboelectric 3.8 运行单元测试时出现 NoClassDefFoundError

Posted

技术标签:

【中文标题】使用 Roboelectric 3.8 运行单元测试时出现 NoClassDefFoundError【英文标题】:NoClassDefFoundError while running unit tests with Roboelectric 3.8 【发布时间】:2018-12-01 18:58:53 【问题描述】:

我的 android 项目有多个模块(一个应用程序和多个库),当我尝试从命令行运行所有单元测试 (Robolectric 3.8) 时

./gradlew testDebugUnitTest

我收到以下错误(不是当我从 Android Studio 运行相同任务时)

java.lang.NoClassDefFoundError: android/content/Context at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) 在 java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3110) 在 java.base/java.lang.Class.getConstructor0(Class.java:3315) 在 java.base/java.lang.Class.getConstructor(Class.java:2108) 在 org.robolectric.RobolectricTestRunner.getHooksInterface(RobolectricTestRunner.java:473) 在 org.robolectric.RobolectricTestRunner.beforeTest(RobolectricTestRunner.java:308) 在 org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:241) 在 org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:123) 在 org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:42) 在 org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 在 org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 在 org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 在 org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 在 org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 在 org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:77) 在 org.junit.runners.ParentRunner.run(ParentRunner.java:363) 在 org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114) 在 org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57) 在 org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66) 在 org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native 方法)在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:564) 在 org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) 在 org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) 在 org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) 在 org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) 在 com.sun.proxy.$Proxy1.processTestClass(Unknown Source) 在 org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:108) 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native 方法)在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:564) 在 org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) 在 org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) 在 org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146) 在 org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128) 在 org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404) 在 org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) 在 org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) 在 java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 在 java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 在 org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) 在 java.base/java.lang.Thread.run(Thread.java:844) 引起: java.lang.ClassNotFoundException:无法加载 android.content.Context 在 org.robolectric.internal.bytecode.SandboxClassLoader.maybeInstrumentClass(SandboxClassLoader.java:174) 在 org.robolectric.internal.bytecode.SandboxClassLoader.lambda$findClass$0(SandboxClassLoader.java:133) 在 org.robolectric.util.PerfStatsCollector.measure(PerfStatsCollector.java:50) 在 org.robolectric.internal.bytecode.SandboxClassLoader.findClass(SandboxClassLoader.java:132) 在 java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:563) 在 java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496) ... 45 更多原因:java.lang.IllegalArgumentException at org.objectweb.asm.ClassReader.(Unknown Source) at org.objectweb.asm.ClassReader.(Unknown Source) at org.robolectric.internal.bytecode.SandboxClassLoader$ClassInstrumentor.isOverridingFinalMethod(SandboxClassLoader.java:502) 在 org.robolectric.internal.bytecode.SandboxClassLoader$ClassInstrumentor.instrumentInheritedObjectMethod(SandboxClassLoader.java:523) 在 org.robolectric.internal.bytecode.SandboxClassLoader$ClassInstrumentor.instrument(SandboxClassLoader.java:395) 在 org.robolectric.internal.bytecode.SandboxClassLoader$InvokeDynamicClassInstrumentor.instrument(SandboxClassLoader.java:1274) 在 org.robolectric.internal.bytecode.SandboxClassLoader.getInstrumentedBytes(SandboxClassLoader.java:271) 在 org.robolectric.internal.bytecode.SandboxClassLoader.lambda$maybeInstrumentClass$1(SandboxClassLoader.java:166) 在 org.robolectric.util.PerfStatsCollector.measure(PerfStatsCollector.java:50) 在 org.robolectric.internal.bytecode.SandboxClassLoader.maybeInstrumentClass(SandboxClassLoader.java:165) ... 50 更多

每个模块上的 build.gradle 文件包含以下配置

testOptions 
    unitTests 
        returnDefaultValues true
    

应用模块定义了几个flavor都在同一个维度上运行

flavorDimensions 'default'

有什么想法吗?

【问题讨论】:

你试过在另一台机器上运行它吗?在我的情况下,错误的测试数量各不相同,是什么让我认为它必须与使用的某些特定版本有关...... 【参考方案1】:

我以前有过那种(不一样的)错误。我认为 NoClassDefFoundError 是在告诉您,您的 Android 应用程序在不同版本的 android 上运行,例如 32 位或 64 位,您需要在 android 项目中添加 jniLibs 文件夹。 p>

在运行时根据设备获取你的库。

搜索有关 JniLibs 的更多信息。

谢谢!!

【讨论】:

谢谢@jigar-fumakiya,您能帮我了解如何将您的解决方案应用于我的问题吗?我的测试在 Java 代码上运行,我没有任何库可以在 jniLibs 路径中列出。【参考方案2】:

它必须与您的依赖项有关。您必须将您使用的依赖项与testImplementation.

例如,我在定义此依赖项时遇到错误:

compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.2"

现在我添加了另一个:

//Used to compile
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.2"
//Used to compile tests
testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.2"

检查你在运行时缺少什么依赖项

./gradlew test --stacktrace

Gradle 将自动生成 html,并组织所有跟踪。

这只是一种可能,如果可行,请告诉我。

更新:仅当您将compileOnly 与缺少的依赖项一起使用时才有效。原因是compileOnly 声明了仅在编译时使用的依赖项,所以我们还需要testImplementation。另一种解决方案是直接使用implementation,这将添加所需的依赖项(因此我们不需要testImplementation)。

【讨论】:

以上是关于使用 Roboelectric 3.8 运行单元测试时出现 NoClassDefFoundError的主要内容,如果未能解决你的问题,请参考以下文章

单元测试运行原理探究

单元测试运行原理探究

使用单元测量器虚拟化反应中的单元重叠问题

编写单元测试用例说明书的依据是啥

单元测试基本框架和8个测试方面

单元测试基本框架和8个测试方面