Back in the day, I could do my dependency injection the old fashioned way — through the constructor. But Robolectric versions above 2.0 put an end to all that. So I created a test project to prove the merits of dependency injection using RoboGuice and Robolectric 2.2.

Versions of Robolectric before 2.0 made it easy, albeit a little ugly, to inject dependencies into activities. There were several ways to do it and the method I preferred didn’t seem inconvenient enough to go through the rigamarole of learning to use a dependency injection framework. I admit, I may have been wrong about that one. Although one could use a test-only setter, I preferred using a constructor to pass in what I needed, which allowed me to get rid of test-only setters in lieu of test-only constructors.

This is a pattern I used most often in making my Android demo app Friday Night Lights, of which I test-drove a large portion using Robolectric 1.2. The code below shows the basic premise of passing in dependencies through the constructor. In this case I need to mock the AppStateUtil in my test class.

public class WelcomeActivity extends Activity{

     private AppStateUtil mAppStateUtil;

	public WelcomeActivity(AppStateUtil appStateUtil) {
		mAppStateUtil = appStateUtil;
	}

	public WelcomePlayerActivity(){
		this(new AppStateUtil());
	}

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        appStateUtil.doSomethingForTheActivity();
    }
}

I couldn’t get this pattern to work using Robolectric 2.2. It uses another way to create an activity and call the activity’s lifecycle methods. In the older version of Robolectric it was nothing to pass needed mocks to the activity’s constructor and then call the lifecycle methods on the activity. Those tests looked like this:

@Mock
private AppStateUtil mockAppStateUtil;
private WelcomeActivity mWelcomeActivity;

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this);
    mWelcomeActivity = new WelcomeActivity(mockAppStateUtil);
    Robolectric.shadowOf(mActivity).create();
}

@Test
public void itCallsSomethingOnTheMock(){
    Mockito.verify(mockAppStateUtil).doSomethingForTheActivity();
}

As long as the activity called the mock at some point during its opening lifecycle, then this test would be satisfied. Try to run the same test with Robolectric 2.2, you’ll find that there is no call Robolectric.shadowOf(mActivity).create(). Instead there is another way to get an activity and that is though an API that builds one. See the following for how Robolectric 2.2 does it:

mActivity = Roblectric.buildActivity(WelcomeActivity.class).create().start().resume().get().

This method essentially creates a new activity and then calls any of the lifecycle methods we wish. In this case we’re calling onCreate(), onStart() and onResume(), before we return the instance of the created class with get();

Notice there is no constructor and therefore no trivial way to inject a dependency as before. We could rectify the situation by calling a setter on an instance variable and setting it with a mock before we need it. This approach requires creating methods that may only be used for testing and if we wanted to call that mock in the onCreate() method, I am not aware of a way to make that happen. We would need to set the instance with a mocked object between the activity being constructed (without passing it into the constructor) and the call to onCreate().

The answer for this is a dependency injection framework like RoboGuice. This takes away the need to pass anything to the constructor of an activity. That is because with RoboGuice the new object gets created at the moment that it’s needed.

All we have to do is annotate the object we’re looking to inject with the @Inject annotation. So then the Activity looks like the following:

public class WelcomeActivity extends RoboActivity{

    @Inject AppStateUtil appStateUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        appStateUtil.doSomethingForTheActivity();
    }
}

Much prettier. But there is a cost to the cleanliness of this code. That is the overhead of setting up and maintaining a dependency injection framework. Setting up RoboGuice requires some extra code in both the main project and the test project. Most of the setup deals with telling our framework how to create the object we would like to inject.

In the example github project, we’re going to have to indicate to the Injector exactly how we new up an instance of AppStateUtil. RoboGuice does this through providers. We tell it to look for a class that implements Provider. This class has the instructions of how to create an object that we need to inject. The relationship between an injected class and its specific provider is set up in another class that implements AbstractModule. This class holds the rolodex of marriage certificates between an injected class and its provider.

As we’ve gone over, the provider knows how to create and instance of an object. In order to let the injector know how to create the AppStateUtil, we can create a provider of that type. Implementing the interface Provider forces us to override a get() method which returns an instance of AppStateUtil.

The provider class:

public class AppStateProvider implements Provider<AppStateUtil> {

	@Override
	public AppStateUtil get() {
		return new AppStateUtil();
	}
}

Then we bind the class type to the instructions of how to create that class. The following is the AbstractModule class that holds the instructions for how to bind these objects.


public class WelcomeGuiceModule extends AbstractModule {

	@Override
	protected void configure() {
	    bind(AppStateUtil.class)
               .toProvider(AppStateProvider.class);
	}
}

AbstractModule forces us to override configure() and that is where we are able to establish the relationship between a class an its provider.

So now, at runtime, when the OS discovers the @Inject tag, it looks for a class that implements AbstractModule. That class says “when you want to create an instance of AppStateUtil, ask the AppStateProvider for it.” The AppStateProvider says “if you’re looking for a new AppStateUtil object, here it is… new AppStateUtil().”

But that’s not all. We have to have a way of making sure that same helpful knowledge is available to us in the test project so that we can use our dependency injection in the test cases as well. After all, that’s really the point isn’t it? We want to inject mock objects in lieu of concrete ones.

We’ll need another class that extends AbstractModule for our test project. This module will use the module that we defined in the main project for its binding definitions, but also it defines a method for binding an injected class to a mock object.

Taking an example from the project, let’s take a look at the WelcomeActivityTests class, which should shed light on how we’re going to inject a mock into our class under test.

@RunWith(RobolectricTestRunner.class)
public class WelcomeFragmentTests {

	@Mock
	private AppStateUtil mockAppStateUtil;

        @Inject
	private WelcomeActivity mWelcomeActivity;

	@Before
	public void setUp() {
		MockitoAnnotations.initMocks(this);
		TestGuiceModule module = new TestGuiceModule();
		module.addBinding(AppStateUtil.class,
                    mockAppStateUtil);
	        TestGuiceModule.setUp(this, module);
		mActivity = Robolectric.buildActivity(
                     WelcomeActivity.class).create().start()
                    .resume().get();
	}

	@Test
	public void itCallsAppStateProvider() {
		verify(mockAppStateUtil)
                     .doSomethingForTheActivity();
	}
}

The setUp() tells the story. After initializing our mock object we create an instance of our AbstractModule, in this case TestGuiceModule, and then add the binding that relates the mockAppStateUtil to the AppStateUtil.class. We’ll go into more detail about this binding in a second. Next we call the static setUp() method on TestGuiceModule. As we’ll see later, this is the method that makes it so the test class knows about the bindings that are defined in the application under test.

You might also notice we’re injecting the class we’re trying to test. This works because in this situation this class is a dependency of the test class.

To see how all this fits together, let’s take a look at the setUp() method of the TestGuiceModule.

public static void setUp(Object testObject, TestGuiceModule module) {
    DefaultRoboModule defaultModule = RoboGuice
        .newDefaultRoboModule(Robolectric.application);
    Module welcomeModule = Modules.override(defaultModule)
        .with(new WelcomeGuiceModule());
    Module testModule = Modules.override(welcomeModule)
        .with(module);
    RoboGuice.setBaseApplicationInjector(
        Robolectric.application, RoboGuice.DEFAULT_STAGE,
        testModule);
    RoboInjector injector = RoboGuice.getInjector(
        Robolectric.application);
    injector.injectMembers(testObject);
}

Since we didn’t need to inject the WelcomeActivity object in the application we define a new binding with a provider that is defined in the test project. Assuming we have defined a HashMap named bindings, we can iterate through the mapping to bind classes to instances of a mocked object. This is what we did earlier when we called module.addBinding(AppStateUtil.class, mockAppStateUtil).

Finally, we should create a TestGuiceModule.tearDown() method that that resets the base application injector to using the default module. This is done to keep a custom injector from negatively affecting other tests.

public static void tearDown() {
    RoboGuice.util.reset();
    Application app = Robolectric.application;
    DefaultRoboModule defaultModule = RoboGuice.newDefaultRoboModule(app);
    RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE,
		defaultModule);

}

At last our dependency injection framework for custom objects is complete. Peace.