Reputation: 191
It seems to me that building an Activity unit test with Robolectric's lifecycle utilities (starting with Robolectric.buildActivity()
) and spying on the same Activity with a Mockito spy are mutually exclusive.
Because buildActivity()
controls the construction of the Activity object, the only place to add a spy for the Activity is after calling buildActivity()
. However, the spy doesn't function properly when it's added after the fact.
This is especially true when spying for side effects of ActivityController
lifecycle methods such as create()
, start()
and resume()
. I assume this is because the ActivityController holds a reference to the "real" Activity object and not the spy that was added later.
So is there any way to spy an Activity that's being unit tested with Robolectric, such that the spy works properly when calling the lifecycle methods via Robolectric's ActivityController
?
Upvotes: 16
Views: 3544
Reputation: 22637
No reflection. I guess a decade isn't too late?
@Test
fun testRefreshButtonPressed() {
ActivityController.of(spy(MyActivity::class.java), Intent(), Bundle.EMPTY).use { controller ->
val activity = controller.setup().get()
assertNotNull(activity.binding.refresh)
assertEquals(View.VISIBLE, activity.binding.refresh.visibility)
activity.binding.refresh.performClick()
verify(activity).startService(any()) // It works
}
}
Docs for ActivityController
say:
Using ActivityController directly from your tests is strongly discouraged.
but IMHO this is a lot less offensive than reflection.
Upvotes: 0
Reputation: 2802
The answer is using the reflection to replace the "real" Activity
object in ActivityController
.
@Test
public void someTestMethod() throws NoSuchFieldException, IllegalAccessException {
ActivityController<LoginActivity> ac = Robolectric.buildActivity(LoginActivity.class);
LoginActivity spiedActivity = spy(ac.get());
replaceComponentInActivityController(ac, spiedActivity);
ac.create();
// do your work
}
public static void replaceComponentInActivityController(ActivityController<?> activityController, Activity activity)
throws NoSuchFieldException, IllegalAccessException {
Field componentField = ComponentController.class.getDeclaredField("component");
componentField.setAccessible(true);
componentField.set(activityController, activity);
}
I test it by Robolectric
3.1, and it's ok.
Upvotes: 4
Reputation: 3122
At least for the case where the activity is not the object under test, but only a dummy activity which hosts a fragment under test, it is possible to inject a mock into the test activity which can verify interactions with the activity via the communication interface between fragment and activity (following http://developer.android.com/training/basics/fragments/communicating.html).
Upvotes: 1