3

I am new on Android and I am playing around with Robolectric for my unit tests. I am facing the following problem.

I have an activity I want to test.

MainActivity.java

public class MainActivity extends ActionBarActivity
        implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    private NavigationDrawerFragment mNavigationDrawerFragment;

    @Override
    protected void onCreate (Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);

        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));
    }

    @Override
    public void onNavigationDrawerItemSelected (int position) {
        ...
    }
}

This is the test class:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)
public class MainActivityTests {

    private ActivityController<MainActivity> controller;
    private MainActivity activity;
    private MainActivity spy;

    @Test
    public void onCreate_shouldStartNavigationDrawerFragment () {

        controller = Robolectric.buildActivity(MainActivity.class);
        activity = controller.get();
        assertThat(activity).isNotNull();

        spy = spy(activity);
        spy.onCreate(null);

        verify(spy).onCreate(null);
    }
}

But I am getting the following exception:

java.lang.IllegalStateException: System services not available to Activities before onCreate() at line spy.onCreate(null).

I have been googling for hours and I have tried several workarounds (blindly) without any success. May please anyone guide me?

azizbekian
  • 60,783
  • 13
  • 169
  • 249
kazbeel
  • 1,378
  • 19
  • 40

4 Answers4

2

Here's what did the trick for me. I use attach() before getting an activity to spy on. Tested with Robolectric 3.0

private MainActivity spyActivity;

@Before
public void setUp(){

    MainActivity activity = Robolectric.buildActivity(MainActivity.class).attach().get();
    spyActivity = spy(activity);

    spyActivity.onCreate(null);

}
jasbuber
  • 21
  • 1
  • 4
1

You should be driving the activity lifecycle through Robolectric. See: http://robolectric.org/activity-lifecycle/

So for your case you could do:

controller = Robolectric.buildActivity(MainActivity.class);
activity = controller.get();
assertThat(activity).isNotNull();
spy = spy(activity);
controller.create();

Note: it usually doesn't make sense to spy on the activity lifecycle when testing with Robolectric, since you're the one driving it, so you're only testing that your own method calls executed.

Alex Florescu
  • 5,096
  • 1
  • 28
  • 49
  • I will give a try to this approach this evening. My point testing the `onCreate` method is just to assure that `NavigationDrawerFragment` is created, not the lifecycle of the activity. – kazbeel May 26 '15 at 05:16
  • 2
    I think also that `Mockito` `spy` and `PowerMock` should be used only in exceptional cases. As for the past two years I didn't use any `spy` or `PowerMockito` with `Robolectric` – Eugen Martynov May 26 '15 at 05:33
  • The spinnet you puspose does not work since the spied activity is not the one handled by the controller. Thanks for the help anyway. I guess I will give up trying to check whether the fragment is setup or not. – kazbeel May 26 '15 at 15:53
  • 2
    I really don't understand why I hear people saying that we shouldn't be 'mocking/spying' on the activities we are testing. Sometimes these activities call functions (like getApplication( ) ) that cause errors. I just want to have my activity return a mocked application object but I can't do that without mocking the activity under test. – Caleb Jun 17 '15 at 19:38
  • @Davis, this probably belongs in a different question, where it could be explored in some more detail. There may be other better ways to achieve what you want to accomplish. – Alex Florescu Jun 17 '15 at 19:42
1

If interested in using exactly the same controller, and work with a spy of the activity, you could modify the inner class of the controller via Reflection, check this method:

public static <T extends Activity> T getSpy(ActivityController<T> activityController) {
    T spy = spy(activityController.get());
    ReflectionHelpers.setField(activityController, "component", spy);
    return spy;
}

The ReflectionHelper is available in Robolectric (tested on Robolectric 4.2). Then it is initialized like this:

controller = Robolectric.buildActivity(MainActivity.class);
activity = getSpy(controller.get());

Hope this helps.

Adrián Rivero
  • 161
  • 1
  • 9
0

It means that you have to first call the onCreate() method. It has to be the very first called method.

slanecek
  • 808
  • 1
  • 8
  • 24