如何在Kotlin中使用await()测试协程?

eqoofvh9  于 2023-06-24  发布在  Kotlin
关注(0)|答案(1)|浏览(230)

我尝试用mock为包含await()的suspend函数创建测试。到目前为止,我已经尝试了以下代码,但测试从未结束。

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)
    }
35g0bw71

35g0bw711#

你已经模拟了整个auth.signInWithEmailAndPassword(email, password).await(),你实际上想做的只是模拟auth.signInWithEmailAndPassword(email, password)。如果在every或coEvery语句中链接多个调用,可能会发现结果是意外的。尝试从对coEvery的调用中删除对await的调用。

val task: Task<AuthResult> = mockk()
every { auth.signInWithEmailAndPassword(email, password) } returns task
coEvery { task.await() } returns authResult

为了进一步解释这一点,当使用mockk模拟一个调用时,当您命中mocking语句时,执行的行只运行一次,但它被您定义的mock对象捕获。mockk框架知道它处于定义上下文中(coEvery),并且可以推断出它应该将此调用快照为它是什么(行为的定义,而不是对某些响应的期望)。然而,await调用并不是对mock本身的调用,尽管它看起来像mock。它是对auth调用返回的任务的调用。
everycoEvery语句中使用的mockk lambda的问题很容易被误认为是“只捕获”了里面的语句,但实际上,只捕获了对任何mock或spy的调用。实际上,其他的都是执行的。如果执行是不需要的,它仍然会被执行,即使它在上下文中没有意义,或者无法返回任何东西。在您的例子中,通过包含await,您实际上是在等待调用响应,但您当时从未定义过。只有在执行完整个上下文之后,authResult才被分配给内部的调用。
我建议在一个withTimeout块中运行测试用例,以避免它们被占用:https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout.html

相关问题