Robolectric: unit testing Android apps

By: on December 31, 2010

I’ve been playing around with doing some testing of my Android apps, and it turns out to be a little bit more difficult than I’d hoped, but with the aid of some tools it can be brought back under control. Given Android apps are basically just Java, the usual choice would be JUnit. Android needs a bunch of extra classes that aren’t part of the standard Java setup, but given the SDK provides an android.jar it should just be a matter of plugging that into your test stage, right?

Ok, it’s not quite that simple. Doing this gets you “java.lang.RuntimeException: Stub!” rather rapidly. As it turns out, the major purpose of the android.jar is to provide something to compile against, but the actual implementation of the classes is missing, as it’s assumed you’re going to run the app on your device or emulator. Interfacing with either of these is going to be a bit slow and fiddly for unit testing purposes (my netbook for example is perfectly good for Android dev provided you don’t need the emulator…), so that’s out. Something could possibly be worked out using the x86 Android port, but that’s likely to get very complicated very fast, so let’s skip that as well.

Enter Robolectric. With the aid of a bunch of magic behind the scenes, it lets you make your tests work.


For those of you familiar with JUnit and Android, this looks like a pretty conventional test. The only notable difference is the @RunWith at the top to make JUnit use Robolectric. If all you want is to run Android unit tests, you can stop here and pay no attention to the man behind the curtain, but I’m going to delve a bit further.

As it turns out, there’s actually quite a lot of behind-the-scenes magic going on to make this work, which explains why Robolectric takes a little while to start up. In fact, it’s using the Javassist libraries to do significant rewrites to the Android classes and then swapping real classes for shadow classes where available. It feels a bit like overkill for the end results, but there doesn’t appear to be any cleaner way to do this. The rewrites as it turns out are pretty simple and brute-force – make all of the methods in android.* (and a couple of others, but mostly android.*) classes just return the “default” value for whatever their return is (e.g. null, false, etc). This gets the system past the “java.lang.RuntimeException: Stub!” issues, but now the libraries aren’t really doing anything, your tests will probably fail rapidly, and that’s where shadow classes come in.

Shadow classes act as a replacement implementation of the android.* classes that have just been gutted by the rewrite stage. In other words, you asked for a Foo object, and you get a ShadowFoo object instead, and ShadowFoo implements (at least some of) the methods of Foo. As Robolectric is still at the pre-1.0 stage, there’s a few things missing, but quite a lot of code will work ok. When it doesn’t, Shadow classes have been designed to be simple to implement, without needing to know anything about the low-level class rewriting stuff. Here’s the code for ShadowMenuInflater for example:

MenuInflater only has the one proper method to implement, and most of the hard work is in the inflateMenu() code in the resource loader, but it suffices to demonstrate a number of items of note in a shadow class. The most important item here is the @Implements, which indicates which other class this is a shadow of, shortly followed by the @Implementation which marks a method as a shadow method. Non-shadow methods can also be present, either for internal use or testing purposes. In the latter case, shadowOf() needs to be called to get a reference to the shadow class. Referring back to the example earlier with Foo and ShadowFoo, the ShadowFoo object you got back is pretending that it’s a Foo object, so you don’t get access to any of the ShadowFoo-specific methods, but shadowOf() lets you get a direct reference to the shadow object and so get to those methods. The __constructor__ is a rare case of a non-annotated override method, in this case overriding the constructor method for MenuInflater (the rewriting magic looks for a method called that when making the object), probably because of constructors being rather special methods in general in Java. __constructor__ should always return void. The one major missing item here is @RealObject – if a variable is annotated with @RealObject, it will be set to a reference to the “real” object i.e. the thing we’re shadowing (pre-rewrites), which is useful in some contexts.

This should be enough to be getting on with, but there’s also a sample project demonstrating the use of the system available, and if all else fails the developers of the project are responsive and the project is undergoing rapid development, so bug reports are getting good response times (even over the Xmas break!)

FacebookTwitterGoogle+

5 Comments

  1. Christian Williams says:

    Tom, are you seeing slow start-up times every time you run tests? Robolectric should be caching the rewritten android classes after the first test run for quick subsequent startup. Currently we save them to a dir named “tmp” inside your current working directory.

    Glad that you’re finding Robolectric useful!

  2. Tom Parker says:

    @Christian: The tests get faster after the first run, and I’m seeing the cached tmp/ directory. Major issue here is that I’m developing on a netbook which probably has a lot less processing capability than most people are used to. There’s only so much that little N450 can do…

    Thanks for all your hard work!

Post a comment

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>