1

I want to test a MVP-Pattern. So i have a Presenter class, which shall invoke the View methods when certain buttons are clicked. Now I want to verify, that the Presenter really invokes the Method, so I wrote these tests:

Test Class:

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;

import static org.mockito.Mockito.verify;

  @RunWith(MockitoJUnitRunner.class)
  public class MainPresenterTest {

    @Mock
    private MainContract.View view;

    private MainPresenter presenter;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        presenter = new MainPresenter(view);
    }

    @Test
    public void handleScanButtonClicked() {
        presenter.handleScanButtonClicked();
        verify(view).showScanScreen();
    }

    @Test
    public void handleBackButtonClicked() {
        presenter.handleBackButtonClicked();
        verify(view).showMainScreen();
    }
}

Presenter Class:

public class MainPresenter implements MainContract.Presenter {

    private final MainContract.View view;

    public MainPresenter(MainContract.View view) {
        this.view = view;
    }

    @Override
    public void handleScanButtonClicked() {
        view.showScanScreen();
    }

    @Override
    public void handleBackButtonClicked() {
        view.showMainScreen();

    }
}

MainContract Interface

public interface MainContract {

    /** handles UI-interaction **/

    interface Presenter {

        void handleScanButtonClicked();

        void handleBackButtonClicked();

    }

    /** handles showing/hiding UI-elements and screens **/

    interface View {

        void showScanScreen();

        void showMainScreen();

        void showWebViewScreen();
    }
}

My Testing Dependencies in build.gradle:

testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-all:1.10.19'

Test Error Message:

Wanted but not invoked:
view.showMainScreen();
-> at com.whatsthat.androidapp.MainPresenterTest.handleBackButtonClicked(MainPresenterTest.java:36)
Actually, there were zero interactions with this mock.

Wanted but not invoked:
view.showMainScreen();
-> at com.whatsthat.androidapp.MainPresenterTest.handleBackButtonClicked(MainPresenterTest.java:36)
Actually, there were zero interactions with this mock.

    at com.whatsthat.androidapp.MainPresenterTest.handleBackButtonClicked(MainPresenterTest.java:36)
    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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    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.AppMainV2.main(AppMainV2.java:131)



Wanted but not invoked:
view.showScanScreen();
-> at com.whatsthat.androidapp.MainPresenterTest.handleScanButtonClicked(MainPresenterTest.java:30)
Actually, there were zero interactions with this mock.

Wanted but not invoked:
view.showScanScreen();
-> at com.whatsthat.androidapp.MainPresenterTest.handleScanButtonClicked(MainPresenterTest.java:30)
Actually, there were zero interactions with this mock.

    at com.whatsthat.androidapp.MainPresenterTest.handleScanButtonClicked(MainPresenterTest.java:30)
    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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    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.AppMainV2.main(AppMainV2.java:131)


Process finished with exit code -1

My Project Structure

project structure

When I now run the tests without an AVD running, they will fail. I want to run those tests on my local machine only, and there should be no interactions with Android specificly, right? But to let the tests pass, I need to start an AVD, and build + deploy the APK.

Why is this, and how can I run those kind of tests properly on my local machine?

Thanks in advance

Evoxx
  • 51
  • 5
  • Hi, can you post your error messages. – Ezio Jan 28 '18 at 15:57
  • if `View` a concrete class or an abstraction (abstract class or interface). Sounds like the presenter is tightly coupled to a concretion that has knock on effects when exercised during tests. – Nkosi Jan 28 '18 at 16:05
  • @Ezio, i have added them for you. – Evoxx Jan 28 '18 at 16:20
  • 1
    @Nkosi MainContract.View is only an Interface, not extending another one. There is no coupling to any conrecte class in Presenter aswell. It only gets a MainContract.View interface injected in the constructor. – Evoxx Jan 28 '18 at 16:24
  • 1
    What happens if you try to mock the view manually? Also not seeing any annotations on the Test class – Nkosi Jan 28 '18 at 17:05
  • Thanks for the hint, i have added @RunWith(MockitoJUnitRunner.class) to the MainPresenterTest. Now I can make the Tests pass! But there is a problem remaining, I have to rebuild manually everytime I make a change. Otherwise the Test would still fail even if i made changes that would make it pass. – Evoxx Jan 28 '18 at 17:30
  • 1
    @Evoxx glad to help and that you eventually got it work. That is a whole new problem which changes the scope of the original issue. – Nkosi Jan 28 '18 at 17:32
  • Can you also include imports of MainPresenterTest class? – Mustafa Berkay Mutlu Jan 28 '18 at 17:36
  • @Nkosi, Yes, Thank you very much for your help! @ Mustafa: yes, i will do in a minute. – Evoxx Jan 28 '18 at 17:39

2 Answers2

0

You are not telling the framework what it should do when it encounters this line

view.showScanScreen()

and

view.showMainScreen()

do

doNothing().when(view).showScanScreen();
doNothing().when(view).showMainScreen();

You test methods will be like this

@Test
public void handleScanButtonClicked() {
    doNothing().when(view).showScanScreen();
    presenter.handleScanButtonClicked();
    verify(view).showScanScreen();
}

@Test
public void handleBackButtonClicked() {
    doNothing().when(view).showMainScreen();
    presenter.handleBackButtonClicked();
    verify(view).showMainScreen();
}
pvpkiran
  • 25,582
  • 8
  • 87
  • 134
  • Thank you for your answer. I tried it but it did not make a difference. – Evoxx Jan 28 '18 at 16:21
  • did you try to debug, does the code flow goes tot he function you are calling – pvpkiran Jan 28 '18 at 16:24
  • I digged through the method calls now, and it seems that no code inside the Presenter.handleScanButtonClicked() method is executed. Stopping at the beginning of the handleScanButtonClicked() - Test, I reach the end of the Presenter.handleScanButtonClicked() method by pressing Step into once. – Evoxx Jan 28 '18 at 16:47
0

If you want to run your tests on your local machine's JVM, then you need to put them in test package, not in androidTest package. androidTest is used for instrumentation tests and will require Android framework to run.

Check out this post for their differences.

Mustafa Berkay Mutlu
  • 1,929
  • 1
  • 25
  • 37
  • MainPresenterTest is located in the test package. I have added an excerpt from my package location in my question. – Evoxx Jan 28 '18 at 17:22