Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused how to use Mockito for an android test

I'm trying to write a unit test for my android app but having trouble doing what I want with mockito. This is being used in conjunction with Robolectric which I have working just fine and have demonstrated that unit tests work.

I want to test whether or not a button will open a new activity depending on whether there is some bluetooth device connected. Obviously, there is no device connected with bluetooth in my test, however I want to pretend as though there is. The state of the bluetooth connection is stored in my Application class. There is no publicly accessible method to change this value.

So basically the logic in the app is like this:

HomeActivity.java:

//this gets called when the button to open the list is clicked.
public void openListActivity(View button) { 
  MyApplication myApplication = (MyApplication) getApplication();
  if (myApplication.isDeviceConnected() {
      startActivity(new intent(this, ListActivity.class));
   }
}

So to test this I did the following:

TestHomeActivity.java:

@Test
public void buttonShouldOpenListIfConnected() {
    FlexApplication mockedApp = Mockito.mock(MyApplication.class);
    Mockito.when(mockedApp.isDeviceConnected()).thenReturn(true);
    //listViewButton was setup in @Before
    listViewButton.performClick();
    ShadowActivity shadowActivity = Robolectric.shadowOf(activity);

    Intent intent = shadowActivity.getNextStartedActivity();
    assertNotNull(intent); //this fails because no new activity was opened. I debugged this and found that isDeviceConnected returned false.
    ShadowIntent shadowIntent = Robolectric.shadowOf(intent);
    assertThat(shadowIntent.getComponent().getClassName(), equalTo(ListActivity.class.getName()));
}

So my unit test fails because the call (in the activity) to isDeviceConnected returns false even though I thought I told it to return true with the mock framework. I want my test to have this method return true though. Isn't this what mockito does or am I totally mistaken on how to use mockito?

like image 407
Matt Wolfe Avatar asked Dec 20 '12 20:12

Matt Wolfe


1 Answers

That's how mockito works, but the problem is: is your listViewButton using your mockedApp? Seems not, because you're creating mockedApp at the test method and never setting it anywhere. Mockito will not mock the method calls of all instances of Application, only from what you declared as a mock.

I personally don't know how android works with the Application class, but you will have to set it somewhere so listView use your mockedApp instead of what it receives normally.

EDIT After the updated question, you can transform your getApplication in a protected method, spy you listViewButton and make it return your mockedApp. That smells a little bad, but it's one way if you can not set your application mocked object to listViewButton.

EDIT2

Example of using spy in your test using BDDMockito for readability :)

public HomeActivity {
    ...
    protected MyApplication getApplication() {
       // real code
    }
    ...
}

public void TestHomeActivity {
   private HomeActivity homeActivity;

   @Before
   public void setUp() {
       this.homeActivity = spy(new HomeActivity());
   }

   @Test
   public void buttonShouldOpenListIfConnected() {
       // given
       FlexApplication mockedApp = Mockito.mock(MyApplication.class);
       Mockito.when(mockedApp.isDeviceConnected()).thenReturn(true);
       // IMPORTANT PART
       given(homeActivity.getApplication()).willReturn(mockedApp);
       ...
   }
}

After that, your test should work as expected. But I reinforce: Use spy only if you can't inject your mockedApp inside HomeActivity.

like image 143
Caesar Ralf Avatar answered Oct 24 '22 22:10

Caesar Ralf