I commonly use Dependency Injection (DI) to create testable Java code. Dependency injection is simple: instead of having your objects find their own dependencies, you pass them in via the constructor or a setter. One key advantage of this is the ability to easily substitute in stub or mock dependencies during testing.
Naturally, as I started working on an Android application, I tried to apply the same technique. Problems arose when I tried to combine DI with the Android SDK’s Testing and Instrumentation support. In particular, I am yet to find a suitable way to combine DI with functional testing of Android activities via ActivityInstrumentationTestCase2. When testing an activity using the instrumentation support, injection of dependencies is foiled by a couple of factors:
- Constructor injection is impossible, as activities are constructed by the framework. I experimented with various ways of creating the Activity myself, but was unable to maintain a connection with the Android system for true functional testing.
- Setter injection is fragile, as activities are started by the framework as soon as they are created. There is no time to set stub dependencies between the instantiation of the Activity and its activation.
Not ready to give DI away, I scoured the web for existing solutions to this problem. Although I did find some DI libraries with Android support (notably Guice no AOP and roboguice which builds upon it), the only testing support I found was restricted to unit tests. Although roboguice has support for Activities, it relies on being able to obtain a Guice Injector from somewhere — which just shifts the problem by one level of indirection.
Given how complex any DI solution was going to become (if indeed it is possible at all) I decided to step back and consider alternatives. A classic alternative to DI is the Service Locator pattern: where objects ask a central registry for their dependencies. Martin Fowler’s article Inversion of Control Containers and the Dependency Injection pattern compares and contrasts the two patterns in some detail. Most importantly: a Service Locator still allows you to substitute in different implementations of dependencies at test time. The main downside is each class is dependent on the central registry — which can make them harder to reuse. As I’m working with Activities that are unlikely to ever be reused outside of their current application, this is no big deal.
Implementation-wise, I went with the simplest registry that works for me. I found it convenient to use my project’s Application implementation as the registry. In production, the Application onCreate callback is used to create all of the standard dependency implementations. These dependencies are accessed via simple static getters. Static setters are exposed to allow tests to drop in whatever alternative dependencies they desire. A contrived example:
public class MyApplication extends Application { private static IService service; private static ISettings settings; @Override public void onCreate() { super.onCreate(); if (service == null) { service = new ServiceImpl(); } if (settings == null) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); settings = new PreferencesSettings(preferences); } } public static IService getService() { return service; } public static void setService(IService s) { service = s; } public static ISettings getSettings() { return settings; } public static void setSettings(ISettings s) { settings = s; } }
I access the dependencies via the registry in my Activity’s onCreate callback:
public class MyActivity extends Activity { private IService service; private ISettings settings; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); service = MyApplication.getService(); settings = MyApplication.getSettings(); setContentView(R.layout.main); // ... } // ... }
And I wire in my fake implementations in my functional test setUp:
public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity> { private MyActivity activity; public MyActivityTest() { super("com.zutubi.android.example", MyActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); MyApplication.setService(new FakeService()); MyApplication.setSettings(new FakeSettings()); activity = getActivity(); } public void testSomething() throws Throwable { // ... }
After all of the angst over DI, this solution is delightful in its simplicity. It also illustrates that static is not always a dirty word when it comes to testing!