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
?
This is in contrast to using pure Mockito, which often ends up with tests that are the reverse of the logic you are attempting to test. Robolectric works by emulating the native calls the SDK would make on Dalvik, but on the JVM, so it can run much, much faster.
Mockito.spy is the static method that is used to create a ‘spy’ object/wrapper around the real object instance. Similar to Mock, Spies can be created using @Spy annotation.
Robolectric works by emulating the native calls the SDK would make on Dalvik, but on the JVM, so it can run much, much faster. This makes full unit testing and TDD possible. To use the library, add this dependency to your pom.xml: And this annotation above your test Activity:
The Mockito when () method expects a mock or spy object as the argument. As we can also see, the Exception message even describes what a correct invocation should look like.
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.
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).
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