Migrating Junit4 tests to androidx: What causes 'delegate runner could not be loaded'?
我正在将应用程序迁移到androidx,但似乎无法使单元测试正常工作。 我以Google的AndroidJunitRunnerSample为例,该示例已更新为使用新的androidx api。 尝试运行测试时出现以下错误:
1 | java.lang.Exception: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded. Check your build configuration. |
这是我的模块build.gradle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | android { defaultConfig { testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner" } } dependencies { // Test dependencies androidTestImplementation 'androidx.test:core:1.0.0-beta02' androidTestImplementation 'androidx.test.ext:junit:1.0.0-beta02' androidTestImplementation 'androidx.test:runner:1.1.0-beta02' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02' androidTestImplementation"androidx.arch.core:core-testing:2.0.0" androidTestImplementation 'androidx.room:room-testing:2.1.0-alpha01' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02' androidTestImplementation 'org.hamcrest:hamcrest-library:1.3' } |
这是我的测试的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.ext.junit.runners.AndroidJUnit4; @RunWith(AndroidJUnit4.class) public class EntityParcelTest { @BeforeClass public void createEntities() { // Setup... } @Test void someTest() { // Testing here } |
我究竟做错了什么?
从测试类中删除
编辑:好吧,我做了一些更多的测试。我将应用程序迁移到Kotlin,突然间,我注意到测试也开始在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.ext.junit.runners.AndroidJUnit4; @RunWith(AndroidJUnit4.class) // <-- @RunWith + @BeforeClass = Error public class AndroidXJunitTestJava { @BeforeClass public static void setup() { // Setting up once before all tests } @Test public void testing() { // Testing.... } } |
此Java测试失败,并出现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.ext.junit.runners.AndroidJUnit4; @RunWith(AndroidJUnit4.class) // <-- @RunWith + @Before = works? public class AndroidXJunitTestJava { @Before public void setup() { // Setting up before every test } @Test public void testing() { // Testing.... } } |
测试将无错误运行。我需要使用
但是现在我正在使用Kotlin,以下方法(应该等于第一个Java示例)起作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class AndroidXJunitTest { companion object { @BeforeClass fun setup() { // Setting up } } @Test fun testing() { // Testing... } } |
同样,正如亚历山德罗·比埃塞克(Alessandro Biessek)在回答中说,@ Ioane Sharvadze在评论中说的那样,
1 | @Rule val instantTaskExecutorRule = InstantTaskExecutorRule() |
对于Kotlin示例,发生相同的委托运行器错误。这必须替换为
1 | @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() |
在这里解释。
如果您在Kotlin中使用测试规则并将其编写为Java样式,也会收到错误消息
1 2 | @Rule var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java) |
您必须将
1 2 | @get:Rule var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java) |
删除@Test方法时显示的错误消息。
您可以尝试放置@Test方法并运行
1 2 3 4 5 | @Test public void signInTest() { } |
在测试活动时,请确保ActivityTestRule变量是public:
1 2 | @Rule public ActivityTestRule<YourActivity> activityTestRule = new ActivityTestRule<>(YourActivity.class); |
有两种方法可以尝试解决此问题:
解决方案1:
构建->清洁项目。然后构建->重建项目
解决方案2:
有2个具有AndroidJUnit4的软件包
确保您的build.gradle(应用程序)具有此行
1 2 3 4 5 6 7 | android { defaultConfig { .... testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner" .... } } |
并确保在测试类之前,在
改变中
1 2 3 4 | @Test void someTest() { // Testing here } |
至
1 2 3 4 | @Test public void someTest() { // Testing here } |
为我工作。
测试框架实际上提供的信息太少。我遇到了相同的问题,并进入了堆栈跟踪,并发现了以下验证:
因此,您必须声明您的
您需要确保所有带有@BeforeClass批注的类都是public static。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.ext.junit.runners.AndroidJUnit4; @RunWith(AndroidJUnit4.class) public class EntityParcelTest { @BeforeClass public static void createEntities() { // Setup... } @Test public void someTest() { // Testing here } |
您可以使用
Instructs the Kotlin compiler not to generate getters/setters for this property and expose it as a field
1 2 3 | @Rule @JvmField val activityActivityTestRule = ActivityScenarioRule<MainActivity>(MainActivity::class.java) |
如果在添加以下依赖项之后:
1 2 3 4 | testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' |
和
1 2 3 4 5 6 7 | android{ defaultConfig{ ... testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner" ... } } |
则不能使用属于
我仍然不明白为什么不推荐使用
其他一些可能的原因:
-
仅具有
@SmallTest (或中/大)测试方法。用@Test 标记至少一种方法可以解决此问题。 -
setup() /teardown() 方法不是公开的。
对我来说,问题是
我现在不知道原因。
作为一种临时解决方法,您可以例如使用UIAutomator for ActivityTestRule开始您的活动。
也许您尚未在gradle配置文件上更新跑步者?
1 2 3 4 | defaultConfig { ... testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner" } |
另外,AndroidStudio 3.2还提供了一个选项,可以自动将依赖项迁移到AndroidX(重构->迁移到AndroidX ...),这对我来说是有效的。
我修复了此配置:
我的依赖:
1 2 3 4 5 6 | /*Instrumentation Test*/ androidTestImplementation"org.assertj:assertj-core:3.12.2" androidTestImplementation ('androidx.test.espresso:espresso-core:3.2.0',{ exclude group: 'com.android.support', module: 'support-annotations' }) androidTestImplementation"androidx.arch.core:core-testing:2.1.0-rc01" |
在我的
1 2 3 4 5 6 7 8 9 10 | defaultConfig { applicationId"uy.edu.ude.archcomponents" minSdkVersion 22 targetSdkVersion 28 versionCode 1 versionName"1.0" testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArguments clearPackageData: 'true' } |
在测试中,我有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | package uy.edu.ude.archcomponents.repository import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.room.Room import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 import com.example.android.roomwordssample.WordDao import com.example.android.roomwordssample.WordRoomDatabase import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import uy.edu.ude.archcomponents.entity.Word import java.io.IOException @RunWith(AndroidJUnit4::class) class WordDaoTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() private lateinit var wordDao: WordDao private lateinit var db: WordRoomDatabase @Before fun createDb() { val context = InstrumentationRegistry.getInstrumentation().context // Using an in-memory database because the information stored here disappears when the // process is killed. db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java) // Allowing main thread queries, just for testing. .allowMainThreadQueries() .build() wordDao = db.wordDao() } @After @Throws(IOException::class) fun closeDb() { db.close() } @Test @Throws(Exception::class) fun insertAndGetWord() { runBlocking { val word = Word("word") wordDao.insert(word) val allWords = wordDao.getAlphabetizedWords().waitForValue() assertThat(allWords[0].word).isEqualTo(word.word) } } } |
我也使用android插件版本3.4.2。我从这里获取配置
就我而言,我跳过了用
就我而言,定义函数参数是问题所在。
1 2 3 4 | @Test fun foo(string: String ="") { // Causes"Delegate runner could not be loaded" error. // ... } |
我必须执行以下操作。
1 2 3 4 5 6 7 8 | @Test fun foo() { foo("") } fun foo(string: String) { // Can be called from other test functions. // ... } |
就我而言,我通过将所有
如果您将这些方法中的任何一个都隐式/显式地
1 | java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded. |
因此,正确的方法是使用
1 2 3 4 5 6 7 8 9 | @Before public void setUp() { } @BeforeClass public static void init() { } |
代替:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Before void setUp() { } @Before protected void setUp() { } @Before private void setUp() { } @BeforeClass public void init() { } |
在类和超类中,带有
-
参数:
@Test fun dontWork(param: String) {} -
私人访问修饰符:
@Test private fun dontWork2() {}
注意:如果没有
预期的方式可能是:
ClassTest.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 | import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ClassTest : SuperTest() { @Test fun test() {} private fun allowedWithoutAnnotation(paramAllowed: String) {} } |
build.gradle(:mobile)
1 2 3 4 5 6 7 8 9 10 11 12 13 | defaultConfig { ... testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner" } dependencies { ... testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.arch.core:core-testing:2.1.0' } |
GL
我将这个错误归结为只是将乐趣设置为私有,删除此错误对我来说解决了。