Tuesday, 10 March 2015

Unit testing with Robolectric, Mockito and AssertJ on Android Studio

Introduction

Unit testing is an important task for any project, specially now with the grow number of Startups, where the projects should pivot very fast ensuring the existence feature remains usable at the same time.

In this post I will try to explain how to integrate Robolectric with Mockito and AssertJ as help, from the first step and with the sample project stored on GitHub.

To do so, I start with a simple app which calculates the sum of two numbers. You can find it in this branch:

https://github.com/jiahaoliuliu/unitTestingWithAndroidStudio/tree/creatingApp

Installation

We start with the installation of Robolectric, followed by Mockito and then, finally, with AssertJ. To make the code much more cleaner, it is good idea to create first a separated module.

1 Creating separated module

1.1 Click on File -> New module
1.2 Select Java Library and click on Next
1.3 Call it "app-test" and click on Finish

Android studio will take a while to recompile the project and then, a new project called app-test will be created with the follow structure:


The new module comes with the space for the source code but not for the test code. So, we must create one manually.

1.4 Right click on the src folder and create a new directory called test
1.5 Right click on the new folder and create a new directory called java.
1.6 Right click on the java folder and create a new package called "com.jiahaoliuliu.unittestingwithandroidstudio".

This is the final structure:



Note that the name of the package matches with the name of the package on the app module. This will save many imports.

1.7 Open the build.gradle file in the new module and add the follow code to it, at the top, before apply the plugin of java

evaluationDependsOn(":app")

You can see the code on the branch separatedModule:
https://github.com/jiahaoliuliu/unitTestingWithAndroidStudio/tree/separatedModule

1.8 (optional)
The folder lib is not going to be used, so it can be removed also.
Once it is done, the sentence on build.gradle related with this folder could also be removed.

This is how it is going to be look like:

evaluationDependsOn(":app")

apply plugin: 'java'

dependencies {
}

2 Integrating Robolectric

The next step is integrate Robolectric. At the time of writing, Robolectric 2.4 is stable, but 3.0-Snapshot seems to have less errors, so we are going to use robolectric 3.0.

2.1 On the build.gradle of the general project, add the follow line on both build script and all projects, just under jcenter():

maven { url "https://oss.sonatype.org/content/repositories/snapshots" }

2.2 Sync the project
2.3 At the build.gradle of the new module, add dependencies and extra task configuration related with Robolectric inside of the dependencies block:

dependencies {
    def androidModule = project(':app')
    compile project(path: ':app', configuration: 'debugCompile')

    def debugVariant = androidModule.android.applicationVariants.find({it.name == 'debug'})
    compile debugVariant.javaCompile.classpath
    compile debugVariant.javaCompile.outputs.files
    compile files(androidModule.plugins.findPlugin("com.android.application").getBootClasspath())

    compile 'org.robolectric:robolectric:3.0-SNAPSHOT'
    compile 'junit:junit:4.11'
}

tasks.withType(Test) {
    scanForTestClasses = false
    include "**/*Should.class"
    include "**/*Test.class"
    include "**/*Tests.class"
    exclude "**/*IT.class"

}

2.4 Sync the project.

The actual configuration works with the version 1.0.0 of gradle as android tools. If you see the follow error after sync:

Error:(12, 0) No signature of method: com.android.build.gradle.AppPlugin.getBootClasspath() is applicable for argument types: () values: []

Check on the version of gradle used on the main build.gradle file. It should be as follow:

classpath 'com.android.tools.build:gradle:1.0.0'


2.5 Override your own test runner
2.5.1 Go to the src/main/java folder of the new module and remove the class MyClass if exits.
2.5.2 Right click on the package and select New -> Java class. Name it as RobolectricGradleTestRunner.
2.5.3 Copy the code in this branch:
https://github.com/jiahaoliuliu/unitTestingWithAndroidStudio/blob/robolectricIntegration/app-test/src/main/java/com/jiahaoliuliu/unittestingwithandroidstudio/RobolectricGradleTestRunner.java



2.6 Create new test module to check it works.
2.6.1 Go to the folder test/java of the new module and right click on the package.
2.6.2 Select New -> Java class
2.6.3 Rename it as MainActivityTest
2.6.4 The first thing to do is set that it should run with the customised test runner instead of the official one. To do so, add the follow line between the import statement and the declaration of the class:

@RunWith(RobolectricGradleTestRunner.class)

2.6.5 Once it is done, copy the follow code in the test.

The above code could be find here:
https://github.com/jiahaoliuliu/unitTestingWithAndroidStudio/blob/robolectricIntegration/app-test/src/test/java/com/jiahaoliuliu/unittestingwithandroidstudio/MainActivityTest.java

The code of this step could be find in the branch robolectricIntegration of the testing project:
https://github.com/jiahaoliuliu/unitTestingWithAndroidStudio/tree/robolectricIntegration

2.7 (Just in case) Remove old test data
By default, when Android studio creates a new project, it also created a folder called "androidTest" with a class called ApplicationTest in it. This class will require the emulator or the physical device connected to the computer to be run, which is totally against the purpose of using Robolectric.

The easiest way to fix this problem is removing it totally.

3. Integrating Mockito

Once Robolectric is integrated, it is easy to integrate Mockito.
3.1 Goes to the build.gradle file of the module app-test and add the follow line in the dependencies block, just below to the junit test line:

testCompile "org.mockito:mockito-core:1.+"

3.2 Goes to the new module and right click on the package of test/java, select New -> Java class.
3.3 Name it as calculatorTest and copy and paste the follow code:



Which could be find here:

The source code of this branch could be found in the branch mockitoIntegration of the project:

4. AssertJ integration

AssertJ is a tool provided by Square which make the tests a bit more beautiful. To integrate it to the project, do the follow:
4.1 On the build.gradle of the "app" project, add the follow line in the block of dependencies. Repeat, in the module "app", not "app-test"

compile 'com.squareup.assertj:assertj-android:1.0.0'

4.2 Sync

To test it, go to the test MainActivityTest and put the follow line on the simpleTest:

assertThat(mMainActivity).isNotNull();

You must put the follow import to make it works:

import static org.assertj.core.api.Assertions.assertThat;

There are much more things that assertJ could help. Check its official web page:

The code of this step could be find in the branch assertJIntegration of the project:

Conclusion

So far seems easy to integrate Robolectric, Mockito and AssertJ into the Android Studio. Now it is when the fun begins with the unit tests.





1 comment:

  1. do you have any article that talks about how to use mockito and aspect-android in the project? this article is mainly talking about environement setup

    ReplyDelete