首页 > 解决方案 > jUnit4 - 为什么测试可挂起函数失败并出现“RuntimeException:... AndroidJUnit4 无法加载”?

问题描述

当我测试一个用 编写的简单 db 时Room,当 dao 函数用suspend. 错误是:

java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded.

我的实体是:

@Entity(indices = [Index(value = ["user_name"], unique = true)])
class User (
    @ColumnInfo(name = "user_name") var name: String,
    @PrimaryKey(autoGenerate = true) var id: Long = 0
)

如您所见,我想防止数据库存储User相同的user_name内容。

我的道课是:

@Dao
interface UserDao {
    @Insert
    fun insertUser(user: User): Long

    @Delete
    fun deleteUser(user: User)
}

我的测试是:

@Test fun insertUser() {
    val jojo = User("Jojo")
    val id = userDao.insertUser(jojo)

    Assert.assertNotEquals(null, id)
}

// Inserting the same `User` must raise a `SQLiteConstraintException`
@Test(expected = SQLiteConstraintException::class)
fun insertSameUser() {
    val jojo = User("Jojo")

    userDao.insertUser(jojo)
    userDao.insertUser(jojo)
}

那时,所有测试都按预期成功

当我将 dao 类更改为使用suspend函数时会出现问题:

@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User): Long

    @Delete
    suspend fun deleteUser(user: User)
}

我将测试更改如下:

@Test fun insertUser()= runBlocking {
    val jojo = User("Jojo")
    val id = userDao.insertUser(jojo)

    Assert.assertNotEquals(null, id)
}

// Inserting the same `User` must raise a `SQLiteConstraintException`
@Test(expected = SQLiteConstraintException::class)
fun insertSameUser() = runBlocking {
    val jojo = User("Jojo")

    userDao.insertUser(jojo)
    userDao.insertUser(jojo)
}

现在测试可暂停功能失败!

ARuntimeException提出:

java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded.
at androidx.test.ext.junit.runners.AndroidJUnit4.throwInitializationError(AndroidJUnit4.java:92)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:82)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:51)
at androidx.test.ext.junit.runners.AndroidJUnit4.<init>(AndroidJUnit4.java:46)
at java.lang.reflect.Constructor.newInstance(Native Method)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at androidx.test.internal.runner.junit4.AndroidAnnotatedBuilder.runnerForClass(AndroidAnnotatedBuilder.java:63)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at androidx.test.internal.runner.AndroidRunnerBuilder.runnerForClass(AndroidRunnerBuilder.java:153)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at androidx.test.internal.runner.TestLoader.doCreateRunner(TestLoader.java:73)
at androidx.test.internal.runner.TestLoader.getRunnersFor(TestLoader.java:104)
at androidx.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:789)
at androidx.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:543)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:386)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1959)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:72)
... 16 more
Caused by: org.junit.runners.model.InitializationError
at org.junit.runners.ParentRunner.validate(ParentRunner.java:418)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
at androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:43)
at androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:48)
... 18 more

我发现错误来自 test insertSameUser,如果我insertSameUser按照以下方式更改,则不再有错误:

@Test(expected = SQLiteConstraintException::class)
fun insertSameUser() = runBlocking {
    val jojo = User("Jojo")
    userDao.insertUser(jojo)
    userDao.insertUser(jojo)

    Assert.assertEquals(null, "fake")
}

如果我按照此处@RunWith(AndroidJUnit4::class)的建议删除, 则测试失败并显示. 如果我再次添加上面的虚拟测试,那么测试就可以工作了。java.lang.Exception: Method insertSameUser() should be void

有人有想法吗?

依赖项是:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'

    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'

    //Room
    def room_version = "2.2.1"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
    // optional - Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:$room_version"
    // Test helpers
    testImplementation "androidx.room:room-testing:$room_version"
}

标签: androidkotlinautomated-testsjunit4androidjunitrunner

解决方案


推荐阅读