Reputation: 504
I am working with Android project that needs to initialises Firebase token. Mostly working with Kotlin coroutines I would like to implement code line by line, but since there is no way to synchronously request Firebase token I came up with the solution to wrap OnCompleteListener
using following function:
private suspend fun fcmCallbackSuspendWrapper(block: (OnCompleteListener<InstanceIdResult>) -> Unit)
= suspendCancellableCoroutine<Task<InstanceIdResult>> { cont ->
block(OnCompleteListener { response ->
cont.resume(response)
})
}
It basically blocks current thread until callback is called by Task. Then I'm applying said function in my code in following way:
val fcmInstance = fcmCallbackSuspendWrapper { listener ->
fireBaseInstanceIdProvider
.provide() // Returns Firebase instance
.instanceId // Returns Task<InstanceIdResult>
.addOnCompleteListener(listener) // Sets listener to block current thread until completed
}
But now I need to add unit test to the code above and here I encounter several problems:
@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)
But since Task<InstanceIdResult>
is mocked - it's OnCompleteListener<TResult>
is never called and thread is blocked forever, preserving code execution to continue. What can be done in this situation?
Upvotes: 5
Views: 2790
Reputation: 91
Thanks @mcatta your snippet helped me :)
However since FirebaseIntanceId is deprecated, this is the new way to do it :
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
Upvotes: 9
Reputation: 491
I have done it by using Mockk.io, this is my code snippet, with mockito I think you may do the same thing
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
Upvotes: 9