Android Dependency Injection and Testing Libraries
- RoboGuice – This is an adaption of Google’s dependency injection library but for Android
- Robolectric -This is a testing framework/platform which works to remove the need for constant mocking of Android objects. It also works with RoboGuice.
I can’t begin to thank the guy that first introduced me to dependency injection enough. Coming from a C++ world, where objects can’t describe themselves and there is no reflection unless the developer purposefully implements it himself, to a more modern language I often created my own static factories to simulate the @inject pattern. But why reinvent the wheel each and every time if an object can describe itself perfectly to the virtual machine?
If you’ve never seen Dependency Injection (DI) before look at the “simple example” RoboGuice provides on their webpage. At first glance it might seem like a fancy way to clean up the code so that maintainers could focus on just the specifics of the actions within each method. That is a side benefit (a really nice one at that) but only a side benefit. The example does not demonstrate the true power of such a framework in testing and asserting bug-free code.
Without DI in your code base
Let’s take a look at a contrived example of what sort of pain points DI can help conquer and alleviate. Suppose that I have a method which needs some sort of widget that takes user input. How can I test such a method without requiring someone to step through a script (as in written instructions) wherein they manually enter all the different combinations which could result in a real world scenario? That’s expensive and time consuming. I’m not even going to mention the human factor for mistakes (like skipping a test by accident). Oops, I just did (see? So easy to make mistakes!)
If there was a way to instantiate mock constructs without having the parent object’s constructor grow to 20 arguments long I’d love to jump all over it. Thinking back to design patterns 101, factory objects provide a means for decoupling object creation logic from object instantiation. Using a static factory object I could insert a test version whenever I wanted. Take a look:
public abstract class WidgetBase implements IWidget{ protected ISomeObject mSomeObject; public WidgetBase(ISomeObject _obj){ mSomeObject = _obj; } } public AlertWidgetFactory{ static private WidgetBase mTest = null; static private boolean mTestFlag = false; static WidgetBase create(ISomeObject _obj){ if(mTestFlag){ return mTest; } return new AlertWidget(_obj); } static void setTest(WidgetBase _testWidget){ mTest = _testWidget; } static void setTestFlag(boolean _flag){ mTestFlag = _false; } }
Now when I get down to testing my code I can automate everything. During the “setUp” phase of my JUnit unit test I can substitute in some mocked version of “WidgetBase” to return many possible user entries. See how easy to test it makes the following code:
public void someMethod(String _value){ if(_value == ""){ ISomeObject foo = new SomeValueAssigner( _value ); IWidget alert = AlertWidgetFactory.create(foo); } //more code... }
Of course, if I have a different class type I want to produce from a factory, I’m going to have to code up a factory just for that object. (In C++ I’d just template it and have but a single class for a factory. Sometimes templates are a good thing, no? Take that generics!).
With DI in your code
Some might be screaming at me for not using a plain interface instead of an abstract class. There was a point to that. Interfaces can’t describe the constructor and I wanted my object to take parameters in the constructor. But why?
First have a closer look at the code above. Did you notice up above that I’ve got a hard-coded dependency in “SomeValueAssigner?” I’m not truly able to unit test the “someMethod” function as I can’t remove the interdependencies without first creating a factory method for that too! Oh the humanity. How many factory objects are we going to have to create just to test a decently sized code base? Each time I want a new object I’ll potentially have to create for it an interface, abstract base class and/or a factory object. I might also need to add a new method to the factory object for each different constructor it has.
I don’t need to say it but that’s a lot of work. Actually it’s too much work even for the engineers at Google. This is why they came up with Guice in the first place. DI solves a lot of headaches and saves on needing to reinvent the wheel time after time with some limitations (see InjectionPoints on constructor arguments). It isn’t a replacement for your standard design patterns and doesn’t let you avoid passing arguments into a constructor. RoboGuice is just another tool in your toolset (a very powerful and handy tool).
Avoiding the Emulation Stage
Wouldn’t you know it but testing UI components is hard work. Not only do we need to look at the thing in the first place just to see if all the shapes/sizes/colors are displaying correctly but we also need to make sure it does what it’s supposed to do. Then there’s the waiting period while the emulated Android device warms up. Quoting from Robolectric’s website:
Running tests on an Android emulator or device is slow! Building, deploying, and launching the app often takes a minute or more. That’s no way to do TDD. There must be a better way.
I couldn’t agree more. Waiting on the emulator to compile and load the application for a small, tiny fix is painful.
Robolectric lets you iterate faster. Iterating quickly leads to better design. Better design leads to less maintenance problems. Less maintenance problems leads to more time to add new features or refine functionality. All these things lead to a better product experience for the end user.
That is tremendously beneficial to your bottom line. Even though this section is very short it’s nearly inversely proportional to the amount of time it will save you.
Conclusions
RoboGuice let’s you test more easily and adds another tool/idiom to help decouple your code. Robolectric let’s you test faster. Put them together and you get an environment where you are working more efficiently and productively. These are two libraries you shouldn’t be without.
Reference: Android: Libraries you should be using from our JCG partner at the Statically Typed blog.