Panczur
Panczur

Reputation: 651

Unit test passes, but with error

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

Answers (1)

R. Zag&#243;rski
R. Zag&#243;rski

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

Related Questions