IntelliJ运行Android单元测试时提示‘Test events were not received’求助
Android单元测试报错"Test events were not received"排查与修复
我正在学习Android单元测试,用IntelliJ运行测试时失败,提示"Test events were not received",堆栈跟踪无多余细节。试过增删依赖、参考相关方案都没解决,目前跟着Udemy的TDD课程学习。
测试类代码
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flow import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runBlockingTest import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import petros.efthymiou.groovy.utils.BaseUnitTest import petros.efthymiou.groovy.utils.getValueForTest class PlayListViewModelShould:BaseUnitTest() { private val repository: PlayListRepository = mock() private val playList = mock<List<PlayList>>() private val expected = Result.success(playList) private val exception = java.lang.RuntimeException("SOMETHING NOT RIGHT") @OptIn(ExperimentalCoroutinesApi::class) @Test suspend fun getPlaylistsFromRepository()= runTest{ val viewModel = mockSuccesfulCase() viewModel.playlists.getValueForTest() verify(repository,times(1)).getPlaylists() } @OptIn(ExperimentalCoroutinesApi::class) @Test fun emitPlaylistsFromRepository()= runTest { val viewModel = mockSuccesfulCase() assertEquals(expected, viewModel.playlists.getValueForTest()) } @OptIn(ExperimentalCoroutinesApi::class) @Test fun emitErrorWhenRecieveError() { runTest { whenever(repository.getPlaylists()).thenReturn( flow { emit(Result.failure<List<PlayList>>(exception)) } ) } val viewModel = PlayListViewModel(repository) assertEquals(java.lang.RuntimeException("another error"), viewModel.playlists.getValueForTest()!!.exceptionOrNull()) } private fun mockSuccesfulCase(): PlayListViewModel { runBlocking { whenever(repository.getPlaylists()).thenReturn( flow { emit(expected) } ) } return PlayListViewModel(repository) } }
Gradle配置
android { compileSdkVersion 30 defaultConfig { applicationId "petros.efthymiou.groovy" minSdkVersion 21 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } testOptions { execution 'ANDROIDX_TEST_ORCHESTRATOR' unitTests.all { useJUnitPlatform() } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' freeCompilerArgs = freeCompilerArgs + "-Xallow-result-return-type" } testOptions { animationsDisabled = true } buildFeatures { viewBinding true } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.2' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0-alpha04' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'com.jakewharton.espresso:okhttp3-idling-resource:1.0.0' // testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' testImplementation 'org.mockito:mockito-inline:2.21.0' testImplementation 'androidx.arch.core:core-testing:2.1.0' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.8' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' testImplementation "org.mockito:mockito-core:4.3.1" androidTestImplementation "org.mockito:mockito-android:3.10.0" testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1' testImplementation 'org.junit.vintage:junit-vintage-engine:5.9.1' androidTestImplementation 'com.jakewharton.espresso:okhttp3-idling-resource:1.0.0' androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation "org.mockito:mockito-core:4.3.1" androidTestImplementation 'junit:junit:4.13.2' androidTestImplementation "org.robolectric:robolectric:4.3.1" androidTestImplementation('com.schibsted.spain:barista:3.6.0') { exclude group: 'org.jetbrains.kotlin' // Only if you already use Kotlin in your project } }
问题根源与修复步骤
1. 依赖版本混乱是核心问题
- 协程库版本不匹配:
coroutines-core用1.3.9,coroutines-android用1.6.4,coroutines-test用1.3.8,版本差异会导致测试协程环境初始化失败 - Mockito版本冲突:
mockito-inline是2.21.0,mockito-core是4.3.1,mockito-kotlin是4.0.0,跨大版本的依赖会引发兼容性问题 - JUnit混合使用:同时引入JUnit 5(Jupiter)和JUnit 4兼容引擎(Vintage),导致测试运行器冲突
修复依赖配置:
// 在android块上方定义统一版本变量 ext { coroutines_version = "1.6.4" junit_jupiter_version = "5.9.1" mockito_version = "4.8.1" } dependencies { // 保留原有implementation依赖... // 清理测试依赖,统一版本 testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_jupiter_version" testImplementation "org.mockito:mockito-core:$mockito_version" testImplementation "org.mockito:mockito-inline:$mockito_version" testImplementation "org.mockito.kotlin:mockito-kotlin:4.1.0" testImplementation 'androidx.arch.core:core-testing:2.2.0' // 清理androidTest依赖版本 androidTestImplementation "org.mockito:mockito-android:$mockito_version" // 保留其他androidTest依赖... }
2. 测试代码逻辑错误
emitErrorWhenRecieveError中,mock配置在runTest块内,但ViewModel初始化在块外,导致mock逻辑未生效;且断言的异常信息与mock的不一致mockSuccesfulCase使用runBlocking而非测试专用的runTest,可能导致线程阻塞,测试事件无法传递
修复测试代码:
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flow import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import petros.efthymiou.groovy.utils.BaseUnitTest import petros.efthymiou.groovy.utils.getValueForTest class PlayListViewModelShould: BaseUnitTest() { private val repository: PlayListRepository = mock() private val playList = mock<List<PlayList>>() private val expected = Result.success(playList) private val exception = RuntimeException("SOMETHING NOT RIGHT") @OptIn(ExperimentalCoroutinesApi::class) @Test fun getPlaylistsFromRepository() = runTest { val viewModel = mockSuccesfulCase() viewModel.playlists.getValueForTest() verify(repository, times(1)).getPlaylists() } @OptIn(ExperimentalCoroutinesApi::class) @Test fun emitPlaylistsFromRepository() = runTest { val viewModel = mockSuccesfulCase() assertEquals(expected, viewModel.playlists.getValueForTest()) } @OptIn(ExperimentalCoroutinesApi::class) @Test fun emitErrorWhenRecieveError() = runTest { // 将mock配置与ViewModel初始化放在同一个runTest块内 whenever(repository.getPlaylists()).thenReturn( flow { emit(Result.failure<List<PlayList>>(exception)) } ) val viewModel = PlayListViewModel(repository) // 断言正确的异常对象 assertEquals(exception, viewModel.playlists.getValueForTest()!!.exceptionOrNull()) } private fun mockSuccesfulCase(): PlayListViewModel = runTest { whenever(repository.getPlaylists()).thenReturn( flow { emit(expected) } ) PlayListViewModel(repository) } }
3. 检查IntelliJ测试配置
打开Run > Edit Configurations,确认每个测试类的运行器选择JUnit 5 (Jupiter),而非JUnit 4。
内容的提问来源于stack exchange,提问作者Mohamed Rimshad
相关产品推荐
相关产品推荐

