Robolectric无法实例化测试类,但单个测试方法可正常执行
Android单元测试问题:类级运行失败但单方法可执行
问题现象
导入和依赖配置均正确的情况下,Robolectric始终无法实例化测试类,导致整个类的测试无法运行;但单独执行每个测试方法时,均可成功通过。
测试代码
import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class MyTet { @Test fun testAssert() { val a = true assertTrue(a) } }
build.gradle配置
android { namespace "com.zga.android" testOptions { unitTests { includeAndroidResources = true } } } dependencies { api("androidx.datastore:datastore:1.0.0") api "androidx.datastore:datastore-preferences:1.0.0" implementation core implementation dagger.libs implementation room.libs ksp dagger.compiler ksp room.compiler testImplementation project(':test') testImplementation unitTesting testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.11.1' testImplementation 'androidx.test.ext:junit-ktx:1.1.5' }
错误日志
Failed to instantiate test runner class org.robolectric.RobolectricTestRunner java.lang.RuntimeException: Failed to instantiate test runner class org.robolectric.RobolectricTestRunner at androidx.test.ext.junit.runners.AndroidJUnit4.throwInitializationError(AndroidJUnit4.java:129) at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:121) at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:82) at androidx.test.ext.junit.runners.AndroidJUnit4.<init>(AndroidJUnit4.java:56) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Unknown Source) at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source) at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104) at org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder$DefensiveAnnotatedBuilder.buildRunner(DefensiveAllDefaultPossibilitiesBuilder.java:114) at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86) at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70) at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:37) at org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder.runnerForClass(DefensiveAllDefaultPossibilitiesBuilder.java:57) at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70) at org.junit.vintage.engine.discovery.ClassSelectorResolver.resolveTestClass(ClassSelectorResolver.java:66) at org.junit.vintage.engine.discovery.ClassSelectorResolver.resolve(ClassSelectorResolver.java:47) at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$2(EngineDiscoveryRequestResolution.java:135) at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source) at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(Unknown Source) at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(Unknown Source) at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(Unknown Source) at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source) at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(Unknown Source) at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source) at java.base/java.util.stream.ReferencePipeline.findFirst(Unknown Source) at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:189) at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:126) at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:92) at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:83) at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113) at org.junit.vintage.engine.discovery.VintageDiscoverer.discover(VintageDiscoverer.java:42) at org.junit.vintage.engine.VintageTestEngine.discover(VintageTestEngine.java:64) at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152) at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:134) at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:108) at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:80) at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:110) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:110) at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:90) at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:85) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at jdk.proxy2/jdk.proxy2.$Proxy5.stop(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193) at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60) at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65) at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) Caused by: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Unknown Source) at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source) at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:112) ... 62 more Caused by: org.junit.runners.model.InvalidTestClassError: Invalid test class 'com.zga.android.MyTet': 1. No runnable methods at org.junit.runners.ParentRunner.validate(ParentRunner.java:525) at org.junit.runners.ParentRunner.<init>(ParentRunner.java:92) at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:74) at org.robolectric.internal.SandboxTestRunner.<init>(SandboxTestRunner.java:75) at org.robolectric.RobolectricTestRunner.<init>(RobolectricTestRunner.java:109) at org.robolectric.RobolectricTestRunner.<init>(RobolectricTestRunner.java:104) ... 68 more
问题原因
错误日志核心提示是No runnable methods,根源是JUnit4与JUnit Jupiter(JUnit5)注解混用:
- 类上使用JUnit4的
@RunWith(AndroidJUnit4::class)指定测试运行器 - 测试方法使用JUnit5的
org.junit.jupiter.api.Test注解
JUnit4的运行器无法识别JUnit5的@Test,因此运行整个类时会判定无可用测试方法,导致实例化失败;而单独运行方法时,IDE会直接用JUnit5引擎处理,绕过类上的JUnit4配置,因此能正常执行。
解决方案
方案1:统一使用JUnit4
替换测试方法的@Test为JUnit4版本,保留类上的@RunWith配置:
import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class MyTet { @Test fun testAssert() { val a = true assertTrue(a) } }
方案2:统一使用JUnit5(推荐)
移除JUnit4的@RunWith,改用Robolectric的JUnit5扩展:
- 添加Robolectric JUnit5依赖到
build.gradle:
testImplementation 'org.robolectric:robolectric-junit5:4.11.1'
- 修改测试代码:
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.robolectric.junit5.RobolectricExtension import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(RobolectricExtension::class) class MyTet { @Test fun testAssert() { val a = true assertTrue(a) } }
内容的提问来源于stack exchange,提问作者Varian Wrynn
相关产品推荐
相关产品推荐

