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?
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.
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.
To check if a method was called on a mocked object you can use the Mockito. verify method: Mockito. verify(someMock).
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.
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).
Because you tell it to call the method. This
verify(mockedPostman.login(username, password))
should be
verify(mockedPostman).login(username, password)
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