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