You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中含await()的协程如何测试?mockk测试无响应排查

问题:MockK测试含await()的挂起函数时测试无法结束

我尝试用MockK为包含await()的挂起函数编写测试,但测试始终无法结束。请问当前测试方法是否正确?有没有遗漏步骤?

测试代码

private lateinit var repository: AuthRepository
private lateinit var databaseReference: DatabaseReference
private lateinit var auth: FirebaseAuth

@Before
fun setUp() {
    mockkStatic(FirebaseAuth::class)
    auth = mockk()
    databaseReference = mockk()
    every { FirebaseAuth.getInstance() } returns auth
    repository = AuthRepository(databaseReference)
}

@Test
fun login_Successful() = runBlocking {
    val email = "test@example.com"
    val password = "password"
    val authResult: AuthResult = mockk()
    val expectedResult = Resource.Success(authResult)

    coEvery { auth.signInWithEmailAndPassword(email, password).await() } returns authResult

    val result = repository.login(email, password)

    Assert.assertEquals(expectedResult, result)
}

待测试代码

private val auth = FirebaseAuth.getInstance()

suspend fun login(email: String, password: String): Resource<AuthResult> =
    try {
        val authResult = auth.signInWithEmailAndPassword(email, password).await()
        Resource.Success(authResult)
    } catch(e: FirebaseAuthException) {
        Resource.Error(e.errorCode)
    }

问题原因

测试无法结束的核心问题是:你直接在coEvery中mock了await()调用,但signInWithEmailAndPassword返回的AuthResultTask是未被mock的真实对象,它的await()方法会阻塞当前协程——真实的Task没有实际执行环境,会一直处于pending状态,导致测试卡住。

修复方案

需要分两步mock:先让signInWithEmailAndPassword返回一个mock的Task对象,再对这个mock对象的await()方法指定返回值。

修改后的测试代码:

private lateinit var repository: AuthRepository
private lateinit var databaseReference: DatabaseReference
private lateinit var auth: FirebaseAuth

@Before
fun setUp() {
    mockkStatic(FirebaseAuth::class)
    auth = mockk()
    databaseReference = mockk()
    every { FirebaseAuth.getInstance() } returns auth
    repository = AuthRepository(databaseReference)
}

@Test
fun login_Successful() = runBlocking {
    val email = "test@example.com"
    val password = "password"
    val authResult: AuthResult = mockk()
    // 新增:mock Task对象
    val authTask: Task<AuthResult> = mockk()
    val expectedResult = Resource.Success(authResult)

    // 第一步:mock signInWithEmailAndPassword返回mock的Task
    every { auth.signInWithEmailAndPassword(email, password) } returns authTask
    // 第二步:mock Task的await()方法返回预期结果
    coEvery { authTask.await() } returns authResult

    val result = repository.login(email, password)

    Assert.assertEquals(expectedResult, result)
}

额外补充

  • 测试Firebase的Task.await()时,必须确保Task本身是mock对象,否则真实Task会因无执行环境一直阻塞协程
  • 若要测试异常分支(比如FirebaseAuthException),可在coEvery中指定抛出异常:
coEvery { authTask.await() } throws FirebaseAuthException("error_code", "error_message")

内容的提问来源于stack exchange,提问作者Kleini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 08:40:02