Android Unit testing with Junit4 and Mockito

Finally , Android Studio 1.1 and the Android gradle plugin bring support for unit testing . We no longer need to run our tests on an actual device, we will be able to execute the tests against the JVM, making them much faster. This was previously possible using robolectric, but I always found it a hussle to setup on different projects.

So why should we test our code ?

enter image description here

First of all tests (unit tests, integration tests, whatever) are not like cake frosting, You don’t add them at the end. Tests are written during the development process, not at the end of it. So you will need to plan ahead, and write code while constantly having testability in mind. Which is a good “problem” to have.

But, why should you even bother writing unit tests ?

  • unit testing will “force” you to write loosely coupled code
  • gives you confidence in your code / system
  • fast, automated regression
  • easily improve and refactor your code without breaking it
  • provide documentation, because they show how the code should be used and how it should behave

What is basically a unit test :

  • written by the programmer
  • runs fast (no Network, DB, File access)
  • small scope, focused on a smart part of the software

If you are still not convinced, here is why I write tests :

  • too lazy to manually test
  • I hate regression testing
  • don’t trust QA to properly test all the cases
  • don’t trust other developers changes
  • I wan’t to be able to do a change on Friday at 6:00 PM, publish it, and have a relaxing weekend

enter image description here

To get started with unit testing on Android make sure you follow these steps. Basically you need to :

  • make sure you use build tools 1.1.0 or newer
classpath 'com.android.tools.build:gradle:1.1.0'
  • add those dependencies to your build.gradle
 testCompile 'junit:junit:4.12'
 testCompile 'org.mockito:mockito-core:1.9.5'
  • as this is an “experimental feature” for now, go to Settings>Gradle>Experimental and check Enable Unit Testing support
  • create a folder src/test/java/ and place your unit tests there
  • to Run your tests in Android Studio, select Test Artifact : Unit Tests and then right-click your test class or package and select Run

Here is an example from a sample project I made to highlight the benefits of tests in your Android app.

public class DefaultUserManager implements UserManager {
    private static final String KEY_USER = "key_user";
    private final AccountManager accountManager;
    private final SharedPreferences preferences;

    @Inject
    public DefaultUserManager(AccountManager accountManager, SharedPreferences preferences) {
        this.accountManager = accountManager;
        this.preferences = preferences;
    }
   @Override
    public void setUser(@NonNull User user) {
        if (user.getUserName() == null || user.getUserName().isEmpty()) {
            throw new IllegalArgumentException("Username can not be null or empty");
        }
        if (user.getAuthToken() == null || user.getAuthToken().isEmpty()) {
            throw new IllegalArgumentException("Auth token can not be null or empty");
        }
        preferences.edit().putString(KEY_USER, new Gson().toJsonTree(user).toString()).commit();
        Account account = new Account(user.getUserName(), CleanAuthenticator.TYPE);
        accountManager.addAccountExplicitly(account, "no_pass", null);
        accountManager.setAuthToken(account, CleanAuthenticator.TOKEN_WRITE, user.getAuthToken());
    }
}

And now a part of the unit test for it :

@RunWith(MockitoJUnitRunner.class)
public class DefaultUserManagerTest {
    DefaultUserManager testedUserManager;

    @Mock
    SharedPreferences preferences;
    @Mock
    AccountManager accountManager;
    @Mock
    SharedPreferences.Editor editor;

    ///some setup code omitted

    @Test
    public void testSetUserWillPersistToSharedPreferences() {
        //when - adding a new user
        testedUserManager.setUser(theUser());
        //verify - preferences are updated
        verify(preferences, atLeastOnce()).edit();
        //verify - changed to preferences are committed
        verify(editor, atLeastOnce()).commit();
    }

    @Test
    public void testSetUserWillAddAndroidAccount() {
        //when - adding a new user
        testedUserManager.setUser(theUser());
        //verify -that a user was also added to the Android account manager
        verify(accountManager, times(1))
                .addAccountExplicitly(any(Account.class), anyString(), any(Bundle.class));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidUserThrowsException() {
        //when - adding an invalid user
        testedUserManager.setUser(aInvalidUser());
    }
    //some more omitted, boilerplate code...
}

So with 3 small, easy to read and maintain methods, I have tested my setUser() method. This is how the setUser() method is supposed to work, and this is how all the classes interacting with my UserManager.setUser() method expect it to work.

Obviously in time I will need to improve this code, re-factor it, change it, and this can and will break some things. But, I will have the confidence of my unit tests, I’ll just run them in a few seconds, see if anythings is broken, and if not I can publish my changes, and get back to … YouTube.

Of course, there are multiple cases that will need integration tests, and even manual test, but unit tests will find errors early, will save you a ton of time and also drive good design in your code.

Advertisements

About Ovidiu

Passionate Android Developer. What else? Snowboarding. Football. Stuff like that.
This entry was posted in Android. Bookmark the permalink.

One Response to Android Unit testing with Junit4 and Mockito

  1. Thanks for posting this. I was overthinking adding Mockito to Android unit testing, glad to see it’s pretty easy.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s