Mocked object still calling method (Mockito + Kotlin)

3.2k views Asked by At

I'm trying to test this class:

class LoginPresenter(val mPostman: Postman) : ContractLoginPresenter, Validator.ValidationListener {

    private var view: ContractLoginView? = null

    override fun setView(_view: BaseView) {
        view = _view as ContractLoginView
    }

    override fun getValidationListener(): Validator.ValidationListener {
        return this
    }

    override fun onValidationSucceeded() {
        view!!.getContext().showCircularProgressBar()
        mPostman.login(view!!.getUsername(), view!!.getPassword())
    }

For this purpose, I've created this test class:

@RunWith(PowerMockRunner::class)
@PrepareForTest(App::class)
class TestLoginPresenter {

    var mockedPostman = mock(Postman::class.java)
    var mockedComponent = mock(MainComponent::class.java)
    var mockedView = mock(ContractLoginView::class.java)
    var mockedContext = mock(AppCompatActivity::class.java)

    var loginPresenter: LoginPresenter? = null

    @Before
    fun setUp() {
        PowerMockito.mockStatic(App::class.java)
        `when`(App.component).thenReturn(mockedComponent)
        loginPresenter = LoginPresenter(mockedPostman)
        loginPresenter!!.setView(mockedView)
        `when`(mockedView.getContext()).thenReturn(mockedContext)
    }

    @Test
    fun testGetValidationListener() {
        assertEquals(loginPresenter!!.getValidationListener(), loginPresenter)
    }

    @Test
    fun testOnValidationSucceeded() {
        val password = "password"
        val username = "username"
        `when`(mockedView.getPassword()).thenReturn(password)
        `when`(mockedView.getUsername()).thenReturn(username)
        //Mockito.doNothing().`when`(mockedPostman).login(anyString(), anyString())

        loginPresenter!!.onValidationSucceeded()
        verify(mockedPostman.login(username, password))
    }

Which is failing with the following stacktrace:

java.lang.NullPointerException
    at br.com.tyllt.controller.Postman.login(Postman.kt:26)
    at br.com.tyllt.presenter.LoginPresenter.onValidationSucceeded(LoginPresenter.kt:43)
    at br.com.tyllt.presenter.TestLoginPresenter.testOnValidationSucceeded(TestLoginPresenter.kt:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)


Process finished with exit code -1

The place that the stacktrace point is the last line of postman's login fun:

open class Postman(val mMessenger: TylltEndPoint) {

    fun login(email: String, password: String) {
        val user = User()
        user.email = email
        user.password = password

        mMessenger.login(user).enqueue(ResponseWrapperCallback(EventCatalog.e0014))
    }

Once postman is being mocked, why in the world is it calling the actual implementation of login() fun?

2

There are 2 answers

0
Thomas Kabassis On

Apart from having the Postman class as open, you also need to have the login method as open in order to be able to mock it.

So the Postman class needs to be:

open class Postman(val mMessenger: TylltEndPoint) {

    open fun login(email: String, password: String) {

        // ...

    }

}

This is such a common issue/complaint amongst users, that there is also an official all-open plugin that does this everywhere for you (if you don't care to strictly adhere to the principle of prohibiting inheritance unless you design for it).

1
Thomas Keller On

Because you tell it to call the method. This

verify(mockedPostman.login(username, password))

should be

 verify(mockedPostman).login(username, password)