Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Robolectric buildActivity() with Mockito spy?

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?

like image 894
espressoexcess Avatar asked Oct 16 '13 16:10

espressoexcess


People also ask

Why use robolectric instead of Mockito?

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.

What is @Mockito spy in Java?

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.

How do I use robolectric?

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:

What does the Mockito when () method Expect?

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.


2 Answers

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.

like image 67
Riki Avatar answered Oct 22 '22 03:10

Riki


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

like image 1
dschulten Avatar answered Oct 22 '22 03:10

dschulten