Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocked object still calling method (Mockito + Kotlin)

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?

like image 463
E. Fernandes Avatar asked Jan 03 '17 01:01

E. Fernandes


People also ask

Can you call method on mocked object?

Mockito allows us to partially mock an object. This means that we can create a mock object and still be able to call a real method on it.

Does Mockito work with Kotlin?

Mockito has been around since the early days of Android development and eventually became the de-facto mocking library for writing unit tests. Mockito and Mockk are written in Java and Kotlin, respectively, and since Kotlin and Java are interoperable, they can exist within the same project.

How do you know if a mock method is called?

To check if a method was called on a mocked object you can use the Mockito. verify method: Mockito. verify(someMock).

Does Mockito spy call real method?

A mock does not call the real method, it is just proxy for actual implementations and used to track interactions with it. A spy is a partial mock, that calls the real methods unless the method is explicitly stubbed. Since Mockito does not mock final methods, so stubbing a final method for spying will not help.


2 Answers

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).

like image 137
Thomas Kabassis Avatar answered Oct 16 '22 09:10

Thomas Kabassis


Because you tell it to call the method. This

verify(mockedPostman.login(username, password))

should be

 verify(mockedPostman).login(username, password)
like image 33
Thomas Keller Avatar answered Oct 16 '22 11:10

Thomas Keller