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