Testing on Android

For a long time as a memeber of the Android dev community I had felt that testing on Android was far more difficult than it needed to be. Recently Google has given us a new gift in the form of experimental unit test support in Android Studio. This is an excellent step in the right direction and I am excited to see what comes of this.

In the meantime here are some tips that have helped me improve the test coverage of my Android applications.

Look at the tests for the libraries you use everyday

All of these projects have excellent test coverage. Some are android specific projects and others are just standard java projects but they all provide a wealth of information about testing. Having trouble understanding how to mock an object? Dont know how to bend some Observable to your will? Look at the test code in that particular project and you will be surprised at the wealth of knowledge to be had.

Structure your code in a way that minimizes its dependency on Android frameworks

This was mentioned recently in the new podcast series Fragmented but it deserves mentioning again. When I first start an Android application I let Android Studio generate the core application structure. But before writing any code I also add a second module named core-lib. This is a standard java library not an Android library. I almost always start my projects writing domain classes, networking, and business logic that do not rely at all on any Android frameworks. In doing so I have seen the following benefits.

  • SPEED
    • you dont have to spin up an emulator to run these tests. This allows you to iterate faster
  • JUNIT4+
    • in the past the Android platform only supported older versions of Junit (this has recently been remedied). This ensures you can always use the latest and greatest testing libraries
  • PORTABILITY
    • in keeping more code as a standard Java library you can increase your test coverage and position yourself to re-use more code across many applications

Use wrapper interfaces when appropriate

As I write more code in my core-lib I run into situations where I need to interface with some Android framework. One such situation is when I need a class to return a localized string or Android resource. In this case I sometimes write an interface that I can mock in my java tests and then provide an implementation of that interface that takes in the specific Android framework that should back it.

Example

public interface ResourceWrapper {

    String localizedString(String resourceName);

}

public AndroidResourceWrapper implements ResourceWrapper {

    private Context mContext;
    private String packageName;

    public AndroidResourceWrapper(Context context) {
        mContext = context;
        packageName = context.getPackageName();
    }

    @Override
    public String localizedString(String resourceName) {
        int resourceId = mContext.getResources().getIdentifier(name, RESOURCE_STRING, packageName);

        if (resourceId > 0) {
            return mContext.getString(resourceId);
        }
        return "";
    }
}

I can now pass around this interface in my java library and mock this during my core-lib tests. In my Android application whenever I use a class from core-lib I then provide it with my AndroidResourceWrapper class which is backed by the standard Android resources framework.

Hopefully these tips can help you improve the test coverage of your Android applications.