Evolution of unit tests in Android

Ivan Shafran
ProAndroidDev
Published in
6 min readAug 22, 2019

--

Photo by Jukan Tateisi on Unsplash

In the article we take a look at the unit test evolution from beginner to pro-level. “Rate us” dialog is a popular feature, and it will be a good example. Typical “rate us” dialog has requirements like:

  • Should be shown after some condition or user action
  • Has “rate us” button that leads to Google Play
  • Has “remind me later” button that schedules dialog to show after some time(2 months in our case)
  • Has “never show again” button that hides the dialog forever
Android “rate us” implemented via AlertDialog

Zero version: no unit tests

https://imgflip.com/i/383h35

At the beginning of the Android community, unit tests were not so popular. There are several arguments behind this:

  • Tests require time to be implemented and maintained
  • Apps were mostly simple
  • The framework itself isn’t friendly for writing unit tests

First version: my first unit test

But unfortunately, “rate us” dialog is a too vital feature to ignore unit tests. The more users leave reviews, the more new users your app gets. Also, it should not be annoying. Otherwise, people will leave 1-star reviews.

I’ve implemented a sample app that follows the logic described in the introduction. To trigger dialog, a user should click button two times. Check code in the repository.

To code first unit test, I had to do several things.

Learn a little about JUnit Framework

Feel free to skip this chapter if you are familiar with JUnit.

JUnit is the most popular testing framework for Java. It’s included in dependencies by default to all new Android projects:

dependencies {
testImplementation 'junit:junit:4.12'
}

To write a test, you should create a class in test folder which is created by default and contains ExampleUnitTest.java. Usually, if devs test SomeClass , devs will name class with tests SomeClassTest . Moreover, most times, it belongs to the same Java package.

Simple unit test on JUnit

Let’s see a simple test. You should annotate all test methods with org.junit.Test . Android Studio will automatically show the run test button. assertEquals will throw an exception if arguments are not equal. JUnit marks test as passed if it ends without any exception.

Abstract an Android from business logic

In Android, you can’t write unit tests for a class that uses the Android framework. But wait… WHAT???!

Yes, the framework requires a specific environment and you can’t run it on any JVM. In unit tests, all Android classes are mocked to throw an exception. The best that we can do is to force it to return default values instead of throwing an exception.

// In application module build.gradle
android.testOptions {
unitTests.returnDefaultValues = true
}

Back to the dialog, we certainly use Android classes like Activity/Fragment, View, Dialog, and others for “rate us” feature. Therefore we can’t write unit tests without a bit of effort.

First, I created an interface for every Android dependency which I use for “rate us” showing logic.

Second, I created an interface for every Java dependency that can’t be used directly in tests. More specifically, it is System.currentTimeMillis() .

Last, I applied the dependency inversion principle toShowRateUsLogic.

Mocks

Now we should write mock classes for unit tests. I’ll show one mock below. You can check all the mocks here.

First unit test

After hardworking, it is a pleasure to code the first unit test :)

https://mmorpg.org.pl/system/posts/posters/000/030/808/medium/beauty.jpg?1521034537

To be honest, it is not. But I wrote it in the “my first unit test” style. And I’ll fix it in the next chapters.

Second version: code cleaning

The first unit test is cool but if no one can understand it, then it is useless. Therefore I’ve refactored test class:

  1. setUp is marked with @Before annotation. It makes the method to be invoked before every unit test. We’ll move the common test code to setUp method.
  2. A good test method has a meaningful name. It’s better for reading and also it appears in test reports. We’ll rename test1 to onFirstCheckAndOneClickItShouldNotShow .
  3. I’ve added a more complicated test. The test name is too short to express all the information. That’s why we’ll add comments to the method body.
  4. For the last step, we’ll delete unnecessary and wrong time set.

Third version: mocking libraries

As mentioned, we can not use Android classes in unit tests. But almost all our classes do it. And it’s quite boring to create an interface, an implementation and a mock for every class.

Fortunately, there is an alternative way. We can use mocking libraries.

Mockito

Directly to the most frequently used API:

  • Use Mockito.mock(Class) to mock any interface or class
  • Use Mockito.when(instance.method()).thenReturn(value) to mock method call
  • Use Mockito.verify(instance).method() to check if method was called

Shared preferences mock

In my practice, shared preferences class is the most used Android dependency in business logic. Therefore I’ve implemented shared preferences mock library which mimics Android implementation. Now you can use shared preferences in unit tests with one additional line of code ;)

Fourth version: Kotlin

Kotlin is a good language for Android development. And also, it can bring improvements to unit tests code.

  1. We’ll rename the test method name using spaces enclosed in backticks
  2. We’ll move the common preparation code to function with default arguments
  3. We’ll delete unnecessary comments because we can use named arguments
  4. We’ll use mockito-kotlin cause it has a more idiomatic and compact syntax

Fifth version: Spek

Spek is a unit testing framework for Kotlin which supports Specification and Gherkin style.

Personally, the crucial features of Spek are:

  • Ability to structure test due to condition
  • Ability to construct tests on the go(cause test code is a lambda, not a method)

I intentionally will not describe syntax because it is easy to understand. And if you are interested in Spek then check out this link.

Moreover, Spek generates a structured test report in Android Studio.

Bonus part

The article about unit tests in Android will not be full without several mentions. If you have more links to mention, please share them in comments and I’ll add them to the article.

Robolectric

Robolectric is a framework that brings fast and reliable unit tests to Android. Tests run inside the JVM on your workstation in seconds.

It is not a pure unit testing but allows us to test Android APIs without launching a device or emulator. On the other hand, it has a bigger test run time.

Assertion frameworks

‘Rate us’ dialog logic has boolean return value, therefore we used simple assertTrue or assertFalse. But for more complicated tests, there’s not enough flexibility in default assertions.

Hamcrest is a framework for writing matcher objects allowing ‘match’ rules to be defined declaratively.

assertThat(Math.sqrt(-1), is(notANumber()))

AssertJ — fluent assertions java library.

assertThat(frodo.getName()).isEqualTo("Frodo")

Truth makes your test assertions and failure messages more readable. Similar to AssertJ, it natively supports many JDK and Guava types, and it is extensible to others.

assertThat(notificationText).contains("testuser@google.com")

Let’s become friends on Twitter, Github, and Facebook!

Clap, share and follow me if you like it🐱‍💻

--

--