Kotlin 协程中模拟 Android Firebase 任务

Ale*_*kov 4 android unit-testing kotlin

我正在处理需要初始化 Firebase 令牌的 Android 项目。主要使用 Kotlin 协程,我想逐行实现代码,但由于无法同步请求 Firebase 令牌,我想出了使用OnCompleteListener以下函数进行包装的解决方案:

private suspend fun fcmCallbackSuspendWrapper(block: (OnCompleteListener<InstanceIdResult>) -> Unit)
        = suspendCancellableCoroutine<Task<InstanceIdResult>> { cont ->
    block(OnCompleteListener { response ->
        cont.resume(response)
    })
}
Run Code Online (Sandbox Code Playgroud)

它基本上会阻塞当前线程,直到任务调用回调为止。然后我通过以下方式在我的代码中应用所述函数:

val fcmInstance = fcmCallbackSuspendWrapper { listener ->
    fireBaseInstanceIdProvider
        .provide() // Returns Firebase instance 
        .instanceId // Returns Task<InstanceIdResult>
        .addOnCompleteListener(listener) // Sets listener to block current thread until completed
}
Run Code Online (Sandbox Code Playgroud)

但现在我需要在上面的代码中添加单元测试,这里我遇到了几个问题:

@Mock lateinit var instanceIdResultTaskMock: Task<InstanceIdResult>
@Mock lateinit var fireBaseInstanceIdProviderMock: Provider<FirebaseInstanceId>
@Mock lateinit var firebaseInstanceIdMock: FirebaseInstanceId

...

Mockito.`when`(fireBaseInstanceIdProviderMock.provide()).thenReturn(firebaseInstanceIdMock)
Mockito.`when`(firebaseInstanceIdMock.instanceId).thenReturn(instanceIdResultTaskMock)

Run Code Online (Sandbox Code Playgroud)

但由于Task<InstanceIdResult>被嘲笑 - 它OnCompleteListener<TResult>永远不会被调用并且线程被永远阻塞,保留代码执行继续。在这种情况下可以做什么?

小智 8

我已经使用Mockk.io完成了它,这是我的代码片段,使用mockito我认为你可能会做同样的事情

val mockedResult = mockk<InstanceIdResult>()
every { mockedResult.token } returns "token"

val mockedInstanceId = mockk<Task<InstanceIdResult>>()
every { mockedInstanceId.isSuccessful } returns true
every { mockedInstanceId.result } returns mockedResult

val slot = slot<OnCompleteListener<InstanceIdResult>>()
every { mockedInstanceId.addOnCompleteListener(capture(slot)) } answers {
    slot.captured.onComplete(mockedInstanceId)
    mockedInstanceId
}

every { firebaseInstanceId.instanceId } returns mockedInstanceId
Run Code Online (Sandbox Code Playgroud)


小智 6

谢谢@mcatta你的片段帮助了我:)

然而,由于 FirebaseIntanceId 已被弃用,这是执行此操作的新方法:

mockkStatic("com.google.firebase.messaging.FirebaseMessaging")
       
val firebaseMessagingMock = mockk<FirebaseMessaging>()
every { getInstance() } returns firebaseMessagingMock

val mockGetTokenTask = mockk<Task<String>>()
every { firebaseMessagingMock.token} returns mockGetTokenTask

val slot = slot<OnCompleteListener<String>>()
every { mockGetTokenTask.addOnCompleteListener(capture(slot)) } answers {
  slot.captured.onComplete(mockGetTokenTask)
  mockGetTokenTask
}

every {mockGetTokenTask.exception} returns null
every {mockGetTokenTask.result} returns currentToken
Run Code Online (Sandbox Code Playgroud)