14

I'm trying to unit test a presenter that I made with Kotlin coroutines, and this is also my first time using Mockito

Whenever I try to run the unit test I'm getting the following error the first time it tries to do anything with my view while in the coroutine

Exception in thread "main @coroutine#1 @coroutine#2" java.lang.NullPointerException
    at .signin.SignInPresenter$subscribe$1.doResume(SignInPresenter.kt:45)
    at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
    at kotlinx.coroutines.experimental.DispatchedKt.resumeCancellable(Dispatched.kt:208)
    at kotlinx.coroutines.experimental.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:35)
    at kotlinx.coroutines.experimental.CoroutineStart.invoke(CoroutineStart.kt:111)
    at kotlinx.coroutines.experimental.AbstractCoroutine.start(AbstractCoroutine.kt:161)
    at kotlinx.coroutines.experimental.BuildersKt.launch(Builders.kt:68)
    at kotlinx.coroutines.experimental.BuildersKt.launch$default(Builders.kt:61)
    at .signin.SignInPresenter.subscribe(SignInPresenter.kt:42)

This is the relevant part of my presenter, the line referenced in the error is view.showSigninPanel

class SignInPresenter(
    private val view: SignInContract.View,
    private val userRepo: ParseAuthController,
    private val contextPool: CoroutineContextProvider
) : SignInContract.Presenter {

    private val coroutineJobs: MutableList<Job> = mutableListOf()

    override fun subscribe() {
        view.loadBackgroundImage()
        view.setUpSignInPanel()
        view.setUpKeyboardListener()

        coroutineJobs.add(launch(contextPool.Main) {
            if (!userRepo.isAuthenticated()) {
                view.showSignInPanel()
                subscribeToLoginValidation()
                subscribeToPaswordEmailValidation()
            } else {
                view.launchMainActivity()
            }
        })
    }

The call to userRepo.isAuthenticated() is a suspended call

I'm passing in a test coroutine context into my presenter as per this article

https://android.jlelse.eu/mastering-coroutines-android-unit-tests-8bc0d082bf15

class TestContextProvider : CoroutineContextProvider() {
    override val Main: CoroutineContext = Unconfined
    override val IO: CoroutineContext = Unconfined
}

This is my unit test currently

class SignInPresenterTest {

    private lateinit var view: SignInContract.View
    private lateinit var presenter: SignInPresenter
    private lateinit var parseAuthController: ParseAuthController

    @Before
    fun setUp() {
        view = mock(SignInContract.View::class.java)

        parseAuthController = mock(ParseAuthController::class.java)

        presenter = SignInPresenter(
            view,
            parseAuthController,
            TestContextProvider()
        )
    }

    @After
    fun tearDown() {
        presenter.dispose()
    }

    @Test
    fun subscribeNotAuthenticatedShowSignInPanel() {
        runBlocking {
            val expectedResult = false
            `when`(parseAuthController.isAuthenticated()).thenReturn(expectedResult)

            presenter.subscribe()
        }

        verify(view).showSignInPanel()
    }
}

UPDATE: I've been doing some additional tests, and it looks like if I remove the suspend call within my presenter if (!userRepo.isAuthenticated()) it won't crash and I can verify if something has run with Mockito, but this isn't an actual solution... but something with that suspending call is causing problems?

Ben987654
  • 2,534
  • 2
  • 21
  • 41

1 Answers1

0

Problem solved!

The above code is actually correct. I had the wrong Mockito dependency.

I was copying some dependencies from a co-workers repo and they were using this library that provides some Kotlin specific functionality

https://github.com/nhaarman/mockito-kotlin

Either there's something in there breaking support for coroutines, or maybe there's some different requirements to make it work?

Updating to the latest Mockito dependency works.

Ben987654
  • 2,534
  • 2
  • 21
  • 41