Reputation: 651
So, I've got a probelm with one of my unit tests. It completes with green light, but ClassCastException
occures. The class, that I want to test looks like this:
@Singleton
class AuthManager @Inject constructor(val fireBaseAuth: FirebaseAuth) : AuthContract {
companion object {
const val SIGN_IN_SUCCEED = "SIGN_IN_SUCCEED"
const val UNKNOWN_ERROR = "UNKNOWN_ERROR"
}
override fun signIn(email: String, password: String): Single<Result> {
Timber.d("Email: $email, password: $password")
return Single.create({ emitter ->
fireBaseAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener {
if (it.isSuccessful) {
emitter.onSuccess(Result(isSucceed = true, code = SIGN_IN_SUCCEED))
Timber.d("Sign in by email and password succeed")
} else {
val exception = it.exception as FirebaseAuthException?
val result = handleSignInException(exception)
emitter.onSuccess(result)
Timber.d("Sign in by email and password returned error: " +
result.code)
}
}
})
}
private fun handleSignInException(exception: FirebaseAuthException?): Result {
if (exception == null) {
return Result(isSucceed = false, code = UNKNOWN_ERROR)
}
return Result(isSucceed = false, code = exception.errorCode)
}
}
My proposition for unit test looks like this:
class AuthManagerTest {
@Mock
private lateinit var fireBaseAuth: FirebaseAuth
@Mock
private lateinit var authResult: Task<AuthResult>
private lateinit var authManager: AuthManager
private val EMAIL = "[email protected]"
private val PASSWORD = "password"
@Before
fun setupAuthManager() {
MockitoAnnotations.initMocks(this)
authManager = AuthManager(fireBaseAuth)
}
@Test
fun signInByEmailAndPasswordWithSuccessThenCheckIfResultSucceeds() {
whenever(authResult.isSuccessful).thenReturn(true)
whenever(fireBaseAuth.signInWithEmailAndPassword(EMAIL, PASSWORD)).thenReturn(authResult)
doAnswer{
val listener = it.arguments[0] as OnCompleteListener<AuthResult>
listener.onComplete(authResult)
}.`when`(authResult)
.addOnCompleteListener(ArgumentMatchers.any<OnCompleteListener<AuthResult>>())
val expectedResult = Result(isSucceed = true, code = AuthManager.SIGN_IN_SUCCEED)
val testObserver = authManager.signIn(EMAIL, PASSWORD)
.test()
testObserver.awaitTerminalEvent()
testObserver
.assertNoErrors()
.assertValue(expectedResult)
}
}
And the error:
io.reactivex.exceptions.UndeliverableException: java.lang.ClassCastException: kotlin.Unit cannot be cast to com.google.android.gms.tasks.Task at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:366) at io.reactivex.internal.operators.single.SingleCreate$Emitter.onError(SingleCreate.java:97) at io.reactivex.internal.operators.single.SingleCreate.subscribeActual(SingleCreate.java:42) at io.reactivex.Single.subscribe(Single.java:2693) at io.reactivex.Single.test(Single.java:3104) at bla.bla.bla.AuthManagerTest.signInByEmailAndPasswordWithSuccessThenCheckIfResultSucceeds(AuthManagerTest.kt:48)
The 48 line is when test()
is called on testObserver
. I have no clue, why error occures.
Upvotes: 1
Views: 1177
Reputation: 20258
Let's have a look at Mockito's Answer interface:
public interface Answer<T> {
/**
* @return the value to be returned
*/
T answer(InvocationOnMock invocation) throws Throwable;
}
As you can see answer
method returns object.
In your code:
doAnswer{
val listener = it.arguments[0] as OnCompleteListener<AuthResult>
listener.onComplete(authResult)
}.`when`(authResult).addOnCompleteListener(ArgumentMatchers.any<OnCompleteListener<AuthResult>>())
answer
doesn't return anything, which by default in Kotlin means return Unit
.
Return Task
as in Task#addOnCompleteListener. Write it as:
doAnswer{
val listener = it.arguments[0] as OnCompleteListener<AuthResult>
listener.onComplete(authResult)
authResult
}.`when`(authResult).addOnCompleteListener(ArgumentMatchers.any<OnCompleteListener<AuthResult>>())
and it should be good.
Upvotes: 3