Android Quality with MVP, Espresso, JUnit, JaCoCo and SonarQube

Niels van Hove
AndroidPub
Published in
4 min readMar 16, 2016

--

In my last post, I explained why you should use SonarQube. In this post, I’m going to show you how to setup an Android app with multiple modules, tested using Espresso and JUnit, with code coverage using JaCoCo, which is displayed in a SonarQube instance.

This app is going to be very stripped down. In fact, it is nothing more than a “Hello World!”. Therefore this post won’t explain what MVP is, or why you should use Espresso and JUnit. I’ll show you how to set them up.

As you take a look at StackOverflow, you’ll see a load of posts explaining little pieces of the puzzle; mostly working with old incompatible versions of the libraries, and showing more code than needed, or using Ant instead of Gradle. It’s a good time to show the complete puzzle. (As of march 2016 that is.)

Espresso

Let’s start at the beginning by creating a new app, and add Espresso to make sure the Hello World is actually displayed. (The name Mejjs of course stands for “MVP Espresso JUnit Jacoco SonarQube”)

Add Espresso to build.gradle:

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.android.support.test:runner:0.4.1'

Also add the TestInstrumentationRunner:

android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}

And finally add the ApplicationTest to the androidTest flavor:

public class ApplicationTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);


@Test
public void appShowsHelloWorld() {
onView(withText("Hello World!")).check(matches(isDisplayed()));
}
}

Now running the command ./gradlew connectedAndroidTest will run the test and will fail the build if the Espresso test fails. Right now we already have a fully tested application. But let’s add a Presenter.

Multiple modules

I’ve seen a lot of MVP introduction repositories that place the Presenter directly in the app module. While this is probably the best for brevity and explanation purposes, it gives way to bad Presenter implementations.

I like constrains, so I prefer a new Java module next to the Android module.

public class MainPresenter {

public void start() {
System.out.println("MainPresenter.start");
}
}

From the MainActivity.onCreate() method we’re calling the MainPresenter.start() method.

Test this app with JUnit; add it to build.gradle:

testCompile 'junit:junit:4.12'

Next add a test for the MainPresenter:

public class MainPresenterTest {

MainPresenter presenter;

@Before
public void setUp() throws Exception {
presenter = new MainPresenter();
}

@Test
public void testStart() throws Exception {
presenter.start();
Assert.assertTrue(true);
}
}

Using Android Studio 2.0 Beta 5, on the testStart() method we can run the test with Coverage support, and we can see also this Presenter class is fully tested.

Code Coverage

Android module

Code Coverage in Android is done with JaCoCo. For the Android module this is easy as pie:

android {
buildTypes {
debug {
testCoverageEnabled = true
}
}
}

When running ./gradlew createDebugCoverageReport 4 files are created:

  1. app/build/reports/androidTests/connected/index.html
  2. app/build/reports/coverage/debug/index.html
  3. app/build/outputs/androidtests-results/connected/TEST-nexus5-app-.xml
  4. app/build/outputs/code-coverage/connected/coverage.ec

The first two are HTML files to display the testresults and the code coverage. The last two files are needed to push the data to SonarQube.

Presenter module

The build.gradle for the Presenter needs a different setup because it isn’t an Android module:

apply plugin: 'java'
apply plugin: 'jacoco'

jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/jacocoHtml"
}
}

Running ./gradlew test jacocoTestReport executes the tests and saves the coverage data:

  1. presenters/build/jacoco/test.exec
  2. presenters/build/jacocoHtml/index.html
  3. presenters/build/reports/tests/index.html

SonarQube integration

Now we’ve got all data available to push to SonarQube. I assume you have SonarQube running on your local device. If not yet, take a look at http://www.sonarqube.org/screencasts2/installation-of-sonar/.

In the toplevel build.gradle, add the SonarQube plugin:

plugins {
id "org.sonarqube" version "1.2"
}
allprojects {
sonarqube {
properties {
property "sonar.language", "java"
property "sonar.sources", "src/main"
}
}
}

In the build.gradle for the Android module, add:

sonarqube {
properties {
property "sonar.java.binaries", "build/intermediates/classes/debug"
property "sonar.jacoco.reportPath","build/outputs/code-coverage/connected/coverage.ec"
}
}

Now this is the place where you’d normally add the Sonar-URL and username/password stuff. Fortunately SonarQube has sensible defaults.
The build.gradle for the Presenters doesn’t need anything, this works out of the box!

Run ./gradlew sonarqube and all data is send to SonarQube. A new project in SonarQube is automatically generated if it doesn’t already exists.

I hope this post helps you configuring SonarQube, and that it helps you improve code quality. You can find the code on Bitbucket.

--

--