8

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?

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
E. Fernandes
  • 3,889
  • 4
  • 30
  • 48
  • Just wondering: did you try building a [mcve] - I mean for your own? Just to prove that the whole "chain" with Kotlin and PowerMock and static mocking works for a super simple example? Before going full scale and your real production code? – GhostCat Jan 03 '17 at 07:31
  • I've been able to user Mockito with Kotlin before. I think it is the first time I attemp to use it with PowerMockito though. I was able to solve the problem using Java. I'll post my answer soon – E. Fernandes Jan 03 '17 at 15:22

2 Answers2

12

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

Thomas Kabassis
  • 1,326
  • 1
  • 12
  • 17
3

Because you tell it to call the method. This

verify(mockedPostman.login(username, password))

should be

 verify(mockedPostman).login(username, password)
Thomas Keller
  • 5,933
  • 6
  • 48
  • 80
  • The verification might be incorrect, but it is not the root cause of the NullPointerException when Postman.login(...) is called during the test. – Thomas Kabassis Oct 13 '19 at 21:03