Best practices for Unit Testing Android Apps with Mockk, Kotest and others
Tags: android, unittest, programming, testing
Published: 2023-05-10 07:57:54

Perhaps you write unit tests, or maybe you don’t. With this article, I’m not aiming to persuade you to do it, rather I’d like to demonstrate how far the world of unit tests has advanced and how much easier it is to write unit tests now, as opposed to how Android developers did it 10 years ago. Please also note that I’ll be talking about testing done by developers and not by QA engineers, so we won’t go into high-level frameworks like Appium.

From the point of view of the [Test Pyramid](https://martinfowler.com/bliki/TestPyramid.html), in this article, I will only describe the lower layer, namely unit tests. However, I’m planning on writing one or two other articles dedicated to UI tests with frameworks like Espresso, as well as code coverage and visual reporting for test results. Right now, I’m not planning on touching on the subject of CI/CD setup as it’s quite a major topic, and mobile developers are often not responsible for it (or not all developers).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8kfergukc1f046b47bz.jpg)

So, unit tests are tests that cover the logic of individual parts of an app, be it ViewModel, Repository, Usecase, DataSource, or other components. Normally unit tests do not test Activity and other Android components, because, in such a case, we would have to run the tests on an Android device or an emulator, while Repository testing can be done on a local computer where your development environment is installed. Of course, there is the **Robolectric** framework, but it rather deals with testing UI components.

Ten years ago, classical unit tests for Android were written using the JUnit framework, and even now it’s the most popular framework for such tests. Let’s look at some of its features.

# JUnit

Currently, we have two versions of this framework, JUnit 4 and JUnit 5. The first letter in the name means that the framework was originally developed to test Java code, but it works perfectly well for Kotlin code, too. Below, you can see several examples of tests written using JUnit 4.

Assume we have a simple ViewModel that includes loadUsers method:

```kotlin
class SimpleViewModel : ViewModel() {

    fun loadUsers(): List<User> {
        return listOf(User(id = "1", userName = "jamie123"))
    }
}
```

This method is very dumb, so all we can do is to check if it returns what we expect it to return. To do this, we can write the following test.

```kotlin
class SimpleViewModelTest {

    private val viewModel = SimpleViewModel()

    @Test
    fun testReturnUsers() {
        val result = viewModel.loadUsers()
        assertEquals(listOf(User(id = "1", userName = "jamie123")), result)
    }
}
```

As you can see, we just check if the result of the method execution equals a certain expected value. [Assert.asserEquals](https://junit.org/junit4/javadoc/4.8/org/junit/Assert.html#assertEquals(java.lang.Object,%20java.lang.Object)) method helps us with this. The [Assert](https://junit.org/junit4/javadoc/4.8/org/junit/Assert.html#assertEquals(java.lang.Object,%20java.lang.Object)) class includes lots of other useful methods for such checks. I recommend going through all the available methods.

Let’s add some logic to our loadUsers method. We add a parameter that will return only adult users (older than 18).

```kotlin
fun loadUsers(onlyAdults: Boolean): List<User> {
    val allUsers = listOf(
        User(id = "1", userName = "jamie123", age = 10),
        User(id = "2", userName = "christy_a1", age = 34)
    )
    return if (onlyAdults) {
        allUsers.filter { it.age >= 18 }
    } else {
        allUsers
    }
}
```

For this method, we can write two tests: the first one will check the upper branch of if (onlyAdults), and the second—the lower branch.

```kotlin
@Test
fun testReturnOnlyAdultsUsers() {
    val result = viewModel.loadUsers(onlyAdults = true)
    assertEquals(
        listOf(
            User(id = "2", userName = "christy_a1", age = 34)
        ), result
    )
}

@Test
fun testReturnAllUsers() {
    val result = viewModel.loadUsers(onlyAdults = false)
    assertEquals(
        listOf(
            User(id = "1", userName = "jamie123", age = 10),
            User(id = "2", userName = "christy_a1", age = 34)
        ), result
    )
}
```

So, everything is quite simple. Unit tests allow us to test various scenarios of the logic within methods. We enter some parameters and check the result with the expected value.

JUnit 4 includes basic testing features. Writing tests with it is simple and convenient.

## JUnit 5

**JUnit 5** has many pleasant additions. Also, the list of key annotations somewhat changed: for example,  `@Before` became `@BeforeEach`, and `@After`—`@AfterEach`. In addition, the package hierarchy totally changed. All main classes and annotations are now available through path `org.junit.jupiter.api.*` . Let’s consider several key features new to **JUnit 5**.

### @DisplayName Annotation

This annotation makes names of your tests more expressive.

```kotlin
@DisplayName("Test return only adults users")
@Test
fun testReturnOnlyAdultsUsers() {
    val result = viewModel.loadUsers(onlyAdults = true)
    assertEquals(
        listOf(
            User(id = "2", userName = "christy_a1", age = 34)
        ), result
    )
}
```

Although Kotlin developers can use such test naming even without a special annotation, using [backticks](https://kotlinlang.org/docs/coding-conventions.html#names-for-test-methods).

```kotlin
@Test
fun `Test return only adults users`() {
		...
}
```

`@DisplayName`’s advantage is that it can also be applied to the class name.

```kotlin
@DisplayName("Tests for SimpleViewModel")
class SimpleViewModelTest {
```

Together with such naming of test methods, we can follow the [GivenWhenThen approach](https://martinfowler.com/bliki/GivenWhenThen.html). So our test will look as follows:

```kotlin
@DisplayName("WHEN pass onlyAdults = true THEN return expected items")
@Test
fun testReturnOnlyAdultsUsers() {
    val result = viewModel.loadUsers(onlyAdults = true)
```

Or like this:

```kotlin
@Test
fun `WHEN pass onlyAdults = true THEN return expected items`() {
		...
}
```

One must admit that now the test name describes its structure more clearly.

### @Disabled Annotation

In addition, if you follow TDD and write a lot of tests that don’t always run on the go, JUnit 5 has a convenient annotation `@Disabled` that allows you to turn off tests that don’t work for some reason or were written for code to be added yet.

```kotlin
@Disabled("Code not implemented yet")
@Test
fun `WHEN pass onlyAdults = true THEN return expected items`() {
```

Remember that turning off failed or unstable tests is a bad practice. You should repair such tests or the code that made them fail at once.

### Parameterized tests `@ParameterizedTest`

Let’s now consider a case when our method `loadUsers` receives enum as input.

```kotlin
fun loadUsers(filter: FilterType): List<User> {
    val allUsers = listOf(
        User(id = "1", userName = "jamie123", age = 10),
        User(id = "2", userName = "christy_a1", age = 34)
    )
    return when (filter) {
        FilterType.ADULT_USERS -> allUsers.filter { it.age > 18 }
        FilterType.CHILD -> allUsers.filter { it.age < 18 }
        FilterType.ALL_USERS -> allUsers
    }
}
```

To check all three conditions, we can write three different tests. Alternatively, we can write a parameterized test that will receive as input a parameters array and expected values and then compare them:

```kotlin
@ParameterizedTest
@MethodSource("testArgs")
fun `WHEN pass onlyAdults = true THEN return expected items`(argAndResult: Pair<FilterType, List<User>>) {
    val result = viewModel.loadUsers(argAndResult.first)
    assertEquals(argAndResult.second, result)
}

companion object {
    @JvmStatic
    fun testArgs(): List<Pair<FilterType, List<User>>> = listOf(
        FilterType.CHILD_USERS to listOf(User(id = "1", userName = "jamie123", age = 10)),
        FilterType.ADULT_USERS to listOf(User(id = "2", userName = "christy_a1", age = 34)),
        FilterType.ALL_USERS to listOf(
            User(id = "1", userName = "jamie123", age = 10),
            User(id = "2", userName = "christy_a1", age = 34)
        )
    )
}
```

In this example, I use an additional annotation `@MethodSource` which will include the method providing our parameters. This method must be static (located in a companion object and having `@JvmStatic` annotation) and return a list or a sequence of parameters. And in the very test method we receive an element of this list. In our case, it’s `Pair<FilterType, List<User>>`. This way the test method becomes as simple as possible: we just call a tested method with the first test argument value and compare the result with the second one.

Besides MethodSource, there are  [numerous other sources](https://junit.org/junit5/docs/current/api/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html) of arguments for parameterized tests.

Probably, you’ve noticed that we have been testing clean methods, with the result depending neither on the condition of the class nor on other classes. The user list was hardcoded into the method. In real life things are always more complicated. So, let’s take a look at a situation when our view model receives the user list from a repository.

```kotlin
class SimpleViewModel(private val usersRepository: UsersRepository) : ViewModel() {

    fun loadUsers(filter: FilterType): List<User> {
        
				val allUsers = usersRepository.getUsers()
				return when (filter) {
            FilterType.ADULT_USERS -> allUsers.filter { it.age > 18 }
            FilterType.CHILD_USERS -> allUsers.filter { it.age < 18 }
            FilterType.ALL_USERS -> allUsers
        }
    }
}
```

The purpose is the same: we want to test the logic of the SimpleViewModel method. But we don’t know (and don’t want to know) what UsersRepository returns. For this, we can use a mock framework, for example, [Mockito](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html).

### Mockito

Mockito and similar frameworks allow us to change the logic of classes and methods which we do not test directly but which are part of the code. In our example, we can simulate the execution of `usersRepository.getUsers()` method when the list of expected Users is returned to us, in order to check the execution of `loadUsers` method. I won’t go into much detail about **Mockito’s** syntax as it has many features and nuances. However, the two key features are: it can replace a method’s returning value; it can also check if the replaced method has been called with the expected parameters. In this case, our test will look like this:

```kotlin
private val mockUsersRepository: UsersRepository = mock()
private val viewModel = SimpleViewModel(mockUsersRepository)

@BeforeEach
fun setup() {

    mockUsersRepository.stub {
        on { getUsers() } doReturn listOf(
            User(id = "1", userName = "jamie123", age = 10),
            User(id = "2", userName = "christy_a1", age = 34)
        )
    }
}

@ParameterizedTest
@MethodSource("testArgs")
fun `WHEN pass onlyAdults = true THEN return expected items`(argAndResult: Pair<FilterType, List<User>>) {
    val result = viewModel.loadUsers(argAndResult.first)
    assertEquals(argAndResult.second, result)
    verify(mockUsersRepository).getUsers()
}
```

As you can see, I create `mockUsersRepository` object using `mock(` function. Then, in `setup()` method, I set up this repository in such a way so that it would return a list of hardcoded values. After this, on top of everything else, I check if, for `mockUsersRepository` object,  `getUsers` method was called with `verify` function, in the test method. It’s important to note that **Mockito** is a very old framework and was originally written for Java. When Kotlin appeared, it turned out that out of the box this framework can work with some limitations, which is why an [extension](https://github.com/mockito/mockito-kotlin), **mockito-kotlin**, was released. The example above was written with it. Another Mockito’s limitation was that it was impossible to mock final classes and static methods, and all classes are final by default in Kotlin. Currently, this is solved through `mockito-inline` but, before, it was a serious limitation. In respect to Java development, there was and is an alternative for Android developers, **PowerMock.**

### PowerMock

This framework was intended to eliminate **Mockito’s** limitations with regard to mocking final, static and private methods. It successfully solves this issue but, at the same time, is a bit harder to use. For example, we can mock a static method as follows:

```kotlin
mockStatic(MyClass::class.java)
when(MyClass.firstMethod(anyString())).thenReturn("Some string");
```

Much of this is now available in **Mockito**. Even more - naturally Kotlin doesn’t have static methods, and it’s a good practice not to create them. Ideal code is code that follows the SOLID and Clean Architecture principles, which means it can be easily mocked and tested. Therefore, we, Android developers, don’t need **PowerMock**’s special features but it’s important to remember that such frameworks exist. Another **Mockito’s** limitation concerns work with coroutines and flows. In such cases, once again, **mockito-kotlin** as well as third-party libraries like [turbine](https://github.com/cashapp/turbine) will help you out. This being said, I’d like to tell you about another alternative to Mockito, which is **Mockk**.

### **Mockk**

[Mockk](https://mockk.io/) is a rather new framework; it was originally designed for Kotlin, although it supports Java as well. Before **mockito-kotlin** was released, using **Mockk** was much easier and beneficial for Kotlin developers, but right now both these frameworks have nearly the same set of features and similar syntax. For example, assume that our function `loadUsers` is no longer synchronous, but rather suspended:

```kotlin
interface UsersRepository {
    suspend fun getUsers(): List<User>
```

Regular Mockito can’t mock it. That’s why need the **mockito-kotlin** extension. With it, our mock will look like this:

```kotlin
@BeforeEach
fun setup() {
    mockUsersRepository.stub { onBlocking { getUsers() } doReturn listOf(User("user_id")) }
}
```

In **Mockk**, the mock of the same function will be this way:

```kotlin
@BeforeEach
fun setup() {
    coEvery { getUsers() } returns listOf(User("user_id"))
}
```

As you can see, it’s a matter of taste.

However, an important distinction between **Mockk** and **Mockito** is that all Mockk’s mocks do not have default values. In Mockito, a method that returns Unit will be expectedly called, and a method that returns a reference type will return **null**. This may cause unfavorable consequences: if we can handle this null value in our production code, it is obvious that the test will work incorrectly hiding the NPE. In **Mockk**, any method of the mock class throws an exception by default, although it is possible to create relaxed mocks in order to achieve a behavior similar to that of Mockito.

```kotlin
private val mockUsersRepository = mockk<UsersRepository>(relaxed = true)
```

In fact, it is an anti-pattern because it can lead to developers missing errors hidden by relaxed mocks. However, it may sometimes be useful not to create mocks for functions that return Unit. For this, **Mockk** has a special parameter:

```kotlin
private val mockUsersRepository = mockk<UsersRepository>(relaxUnitFun = true)
```

It is probably the main difference between **Mockk** and **Mockito**. But it is up to you to decide whether it’s an advantage and whether you should change your framework because of it.

Now that we’ve learned about the frameworks to create mocks for unit tests, I’d like to make the task a bit more complicated. In our example,  `SimpleViewModel` class has only one dependency`usersRepository`, and this repository, in turn, contains only one method. In real life, your tested class can have a dozen of dependencies, with a set of methods closely related to each other. That’s why `viewModel.loadUsers` method may be a lot more complicated. In the example below, we combine several Observables to derive the final result.

```kotlin
fun init() {
		observeUserAvailability()
				.subscribeNext { result ->
						// check result
				}
}

private fun observeUserAvailability(draftPotUuid: String): Observable<UserAvailability> = Observable.combineLatest(
        usersRepository.observeCurrentUser(),
        profileRepository.observeCurrentProfile(),
        kycRepository.observeKycStatus(),
        addressRepository.loadCurrentUserAddress(),
        countryRepository.observeCountries(),
    )
```

To test a set of scenarios for `init()` method, you need to have a set of tests with duplicated methods for mocking `observeCurrentUser`, `observeCurrentProfile` , and others. We could add them into `BeforeEach`method but then, we would want to be careful and not to forget to update default mocks where they are not needed. Nested classes of JUnit 5 can help you solve this issue.

### **Nested classes in JUnit 5**

Using nested (or inner for Kotlin) classes, we can group tests with some common conditions. For example, we can create two groups of tests depending on what `usersRepository.observeCurrentUser()` method returns. In the first case, it will return the correct user `User("user_id")`, while in the second case—a user with incorrect ID `User(null)`:

```kotlin
@DisplayName("Tests for SimpleViewModel")
class SimpleViewModelTest {

    private val mockUsersRepository: UsersRepository = mock()
    private val viewModel = SimpleViewModel(mockUsersRepository)

    @DisplayName("GIVEN userRepository returns correct user")
    @Nested
    inner class MockGroup1 {

        @BeforeEach
        fun setup() {
            mockUsersRepository.stub { on { getCurrentUser() } doReturn User("user_id") }
        }

        @Test
        fun `GIVEN kycRepository returns kycPassed WHEN init viewModel THEN get expected result`() {
            ///
        }
    }

    @DisplayName("GIVEN userRepository returns incorrect user")
    @Nested
    inner class MockGroup2 {

        @BeforeEach
        fun setup() {
            mockUsersRepository.stub { on { getCurrentUser() } doReturn User(null) }
        }

        @Test
        fun `GIVEN kycRepository returns kycPassed WHEN init viewModel THEN get expected result`() {
            ///
        }
    }
```

Following this example, we can extend this approach to grouping tests and create as many nested levels as we need, as long as they remain readable and understandable to us and other members of our team. Try to adhere to the [KISS](https://people.apache.org/~fhanik/kiss.html) principle.

**JUnit 5** and the abovelisted frameworks have many other interesting features. I have described only those that I use myself and which I consider highly useful to us, Android developers.

# TDD vs BDD

As a matter of fact, in the previous examples, we have shifted a bit away from the TDD standards in the meaning that we test not only the operability of our code, but rather check if the code runs according to certain specifications (Given/When/Then). These specifications are our tests, and the syntactic sugar in the form of the possibility to give clear names to the tests using DisplayName and the grouping of the tests by a set of similar attributes helps us clearly formulate these specifications. There is an entire family of frameworks in different languages that allow us to create such specifications: for Java it is Spock, for Ruby—RSpec, and for Kotlin—[Spek](https://spekframework.github.io/spek/docs/latest/) and [Kotest](https://kotest.io) frameworks. Below, I will go into more detail about them.

# Spek

Spek is a BDD framework where we describe the conditions in which the tested object must work and its reaction to these conditions, in detail. We by default make the test body from several units, to differentiate between the areas of responsibility (what we are testing, the conditions and the expected result). If we go back to our SimpleViewModel the tests will look like this:

```kotlin
class SimpleViewModelSpec : Spek({

    val mockUsersRepository: UsersRepository = mock()
    val viewModel by memoized { SimpleViewModel(mockUsersRepository) }

    describe("loadUsers") {

        beforeEachTest {
            // do mocking
        }

        it("should return only adult users if pass filterType: adult users") {
		        val result = viewModel.loadUsers(FilterType.ADULT_USERS)
            assertEquals(listOf(User(id = "2", userName = "christy_a1", age = 34)), result)
            verify(mockUsersRepository).getUsers()
        }
    }
})
```

As you can see, our testing logic is written as a lambda expression, and each test is a kind of mapping from the line, the test name, and the very unit running the test. 

The describe-it style is mainly used to write **Spek** tests. `describe`  structure allows us to create a group of tests describing a specific method, and, in `it`, a specific scenario for the method is written. But it is also possible to use the GivenWhenThen style. Unfortunately, the framework doesn’t have embedded coroutine support, so we have to always use `runBlockingTest { }` for suspended functions. There is a ticket with this [feature request](https://github.com/spekframework/spek/issues/426) on Github, but it seems it was never completed.

**Spek** doesn’t provide its own asserts, mocks or matchers, but it allows you to work with other libraries, such as **Mockk** and **Mockito**.

# Kotest

Another [framework](https://kotest.io) that, in terms of its syntax and behavior, is similar to **Spek**. It has also recently gained popularity among Android developers thanks to its Kotlin-first principle and flexibility. Perhaps, flexibility first and foremost means that you can choose the style to write your tests. Currently, you can choose from [10 styles](https://kotest.io/docs/framework/testing-styles.html). Personally, I prefer **FreeSpec**. The above tests will look like this if written in **Kotest**:

```kotlin
class SimpleViewModelSpec : FreeSpec({

    val mockUsersRepository: UsersRepository = mock()
    val viewModel = SimpleViewModel(mockUsersRepository)

    beforeTest {
        // do mocking
    }

    "WHEN pass filterType: adult users" - {

        val result = viewModel.loadUsers(FilterType.ADULT_USERS)

        "THEN return only adult users" {
            assertEquals(listOf(User(id = "2", userName = "christy_a1", age = 34)), result)
            verify(mockUsersRepository).getUsers()
        }
    }
})
```

The test above works in the FreeSpec style according to the GivenWhenThen principle.

**Kotest** has embedded coroutine support, which is why, as opposed to Spek, you won’t have to use `runBlockingTest { }` for suspended functions.

Test isolation level, which differs from JUnit 4/5, is important here. The values of mocks and the test conditions are not cleared after each test. If you want a behavior similar to JUnit, use `isolationMode = IsolationMode.InstancePerLeaf` flag.

```kotlin
class SimpleViewModelKoTest : FreeSpec({

		isolationMode = IsolationMode.InstancePerLeaf
```

Also, **Kotest** provides an additional [set of asserts](https://kotest.io/docs/assertions/assertions.html) and matchers that may be more convenient than those that we’ve used with **Mockito**. If your project already uses **Mockito** and **JUnit**, that’s alright, as tests written in Kotest can co-exist with **JUnit**.

# Conclusion

Today, there are many unit testing frameworks and libraries for Android apps. I have described only the most popular ones, but there are more. If you work on enterprise Android projects you are most likely used to writing unit tests with some of mentioned in this article technologies. As a rule, large companies use time-tested tools, such as JUnit and Mockito. But, as new Kotlin features continue to be released, numerous new testing libraries and frameworks are appearing. I can definitely recommend you to try out Mockk or Kotest, if you haven’t used them before. They can make your life much easier and improve the quality of your tests.

In the next article, I will tell you about the approaches and best practices used when writing UI tests. Also, please share your own experience in using unit tests in your projects: what frameworks do you use?