Saturday, July 05, 2014

Android programming: injecting a context using Robolectric and Roboguice

I have an Android project where I retrieve instances from a database. These classes are not under control of Roboguice, so I have no injection. If I use Roboguice.getInjector(..) I need a context, that I can supply from a static referenced context that I define elsewhere. However, I want to test the same classes in Robolectric, I need a different context.

I decided to do the following.
I create a ContextProvider like so:
public class ContextProvider {

    protected static Context context;

    public static Context getContext() {
        return context;
    }
}

I make a static reference to this in my app as
ContextProvider.getContext()
The implementation in my app is
public class ContextProviderImpl extends ContextProvider {

    @Inject
    public ContextProviderImpl(Context ctx) {
        context = ctx;
    }
}
and in my Robobuice modules file I have
    bind(ContextProvider.class).to(ContextProviderImpl.class);

In my Robolectric test project, the implementation is
public class TestContextProvider extends ContextProvider {

    public TestContextProvider() {
        context = Robolectric.application.getApplicationContext();
    }
}
and the modules file in the test project has
    bind(ContextProvider.class).to(TestContextProvider.class);
What happens is this:
In Roboguice you can implement a class (not interface) with another class that extends the first. The parent class provides the getter for the context instance, so it's the same in the project and the test project. The implementation sets the context field, in different ways in the project and the test project. I think this is an elegant way to have Roboguice injection in classes that can't have injection in the "normal" way.

There is another way of achieving this: you can use Guice AssistedInject to re-create the instance after you retrieve it from a database. That works, but it's more code and less elegant than this.

No comments: