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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With