Build local unit tests

A local test runs directly on your own workstation, rather than an Android device or emulator. As such, it uses your local Java Virtual Machine (JVM), rather than an Android device to run tests. Local tests enable you to evaluate your app's logic more quickly. However, not being able to interact with the Android framework creates a limitation in the types of tests you can run.

A unit test verifies the behavior of a small section of code, the unit under test. It does so by executing that code and checking the result.

Unit tests are usually simple but their setup can be problematic when the unit under test is not designed with testability in mind:

  • The code that you want to verify needs to be accessible from a test. For example, you can't test a private method directly. Instead, you test the class using its public APIs.
  • In order to run unit tests in isolation, the dependencies of the unit under tests must be replaced by components that you control, such as fakes or other test doubles. This is especially problematic if your code depends on the Android framework.

To learn about common unit testing strategies in Android, read What to test.

Local tests location

By default, the source files for local unit tests are placed in module-name/src/test/. This directory already exists when you create a new project using Android Studio.

Adding testing dependencies

You also need to configure the testing dependencies for your project to use the standard APIs provided by the JUnit testing framework.

To do so, open your app's module's build.gradle file and specify the following libraries as dependencies. Use the testImplementation function to indicate that they apply to the local test source set, and not the application:

dependencies {
  // Required -- JUnit 4 framework
  testImplementation "junit:junit:$jUnitVersion"
  // Optional -- Robolectric environment
  testImplementation "androidx.test:core:$androidXTestVersion"
  // Optional -- Mockito framework
  testImplementation "org.mockito:mockito-core:$mockitoVersion"
  // Optional -- mockito-kotlin
  testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
  // Optional -- Mockk framework
  testImplementation "io.mockk:mockk:$mockkVersion"
}

Create a local unit test class

You write your local unit test class as a JUnit 4 test class.

To do so, create a class that contains one or more test methods, usually in module-name/src/test/. A test method begins with the @Test annotation and contains the code to exercise and verify a single aspect of the component that you want to test.

The following example demonstrates how to implement a local unit test class. The test method emailValidator_correctEmailSimple_returnsTrue()attempts to verify isValidEmail(),which is a method within the app. The test function will return true ifisValidEmail() also returns true.

Kotlin

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class EmailValidatorTest {
  @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"))
  }

}

Java

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

class EmailValidatorTest {
  @Test
  public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"));
  }
}

You should create readable tests that evaluate whether the components in your app return the expected results. We recommend you use an assertions library such as junit.Assert, Hamcrest, or Truth. The snippet above is an example of how to use junit.Assert.

Mockable Android library

When you execute local unit tests, the Android Gradle Plug-in includes a library that contains all the APIs of the Android framework, correct to the version used in your project. The library holds all the public methods and classes of those APIs, but the code inside the methods has been removed. If any of the methods are accessed, the test throws an exception.

This allows local tests to be built when referencing classes in the Android framework such as Context. More importantly, it allows you to use a mocking framework with Android classes.

Mocking Android dependencies

A typical problem is to find that a class is using a string resource. You can obtain string resources by calling the getString() method in the Context class. However, a local test can't use Context or any of its methods as they belong to the Android framework. Ideally, the call to getString() would be moved out from the class, but this is not always practical. The solution is to create a mock or a stub of Context that always returns the same value when its getString() method is invoked.

With the Mockable Android library and mocking frameworks such as Mockito or MockK, you can program the behavior of mocks of the Android classes in your unit tests.

To add a mock object to your local unit test using Mockito, follow this programming model:

  1. Include the Mockito library dependency in your build.gradle file, as described in Set up your testing environment.
  2. At the beginning of your unit test class definition, add the @RunWith(MockitoJUnitRunner.class) annotation. This annotation tells the Mockito test runner to validate that your usage of the framework is correct and simplifies the initialization of your mock objects.
  3. To create a mock object for an Android dependency, add the @Mock annotation before the field declaration.
  4. To stub the behavior of the dependency, you can specify a condition and return value when the condition is met by using the when() and thenReturn() methods.

The following example shows how you might create a unit test that uses a mock Context object in Kotlin created with Mockito-Kotlin.

import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

private const val FAKE_STRING = "HELLO WORLD"

@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {

  @Mock
  private lateinit var mockContext: Context

  @Test
  fun readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    val mockContext = mock<Context> {
        on { getString(R.string.name_label) } doReturn FAKE_STRING
    }

    val myObjectUnderTest = ClassUnderTest(mockContext)

    // ...when the string is returned from the object under test...
    val result: String = myObjectUnderTest.getName()

    // ...then the result should be the expected one.
    assertEquals(result, FAKE_STRING)
  }
}

To learn more about using the Mockito framework, see the Mockito API reference and the SharedPreferencesHelperTest class in the sample code. Also try the Android Testing Codelab.

Error: "Method ... not mocked"

The Mockable Android library throws an exception if you try to access any of its methods with the Error: "Method ... not mocked message.

If the exceptions thrown are problematic for your tests, you can change the behavior so that methods instead return either null or zero, depending on the return type. To do so, add the following configuration in your project's top-level build.gradle file in Groovy:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }