Robolectric strategies

Robolectric is an open-source framework maintained by Google that lets you run tests in a simulated Android environment inside a JVM, without the overhead and flakiness of an emulator. It supports all versions of Android since Lollipop (API level 21).

Many big projects use Robolectric to increase the speed and reliability of their tests and reduce the expenses associated with running tests on real devices or emulators. This includes most Google apps which rely heavily on Robolectric.

Robolectric is not a complete replacement for an emulator because it doesn't support all the features and APIs. For example, Robolectric doesn't have a screen like an emulator does, and some APIs are only partially supported. However, it emulates enough parts of Android to run unit tests and most UI tests reliably.

Testing strategies

There are two types of testing strategies you can pursue with Robolectric: unit testing and UI testing.

Unit testing

Robolectric was conceived as a way to enable "unit testing" in Android apps. For example, you can simulate the launch of an Activity and test the logic inside it, calling all the lifecycle methods.

You can also use Robolectric's fakes (called shadows) as dependencies for unit tests. For example, if your class uses a Bundle or you need to fake a Bluetooth connection.

In general, if you implement a testable architecture you shouldn't need to use Robolectric for unit testing as your code should be testable in isolation, with no dependencies on the Android framework.

UI testing

Robolectric can also run UI tests such as Espresso or Compose tests. You can convert an instrumented test to Robolectric by moving it to the test source set and setting up the Robolectric dependencies.

android {
  testOptions {
    unitTests {
      isIncludeAndroidResources = true
    }
  }
}

dependencies {
  testImplementation("junit:junit:4.13.2")
  testImplementation("org.robolectric:robolectric:4.13")
}

Any UI test present in the test source set runs with Robolectric.

import androidx.test.espresso.Espresso.onView

@RunWith(AndroidJUnit4::class)
class AddContactActivityTest {
    @Test
    fun inputTextShouldBeRetainedAfterActivityRecreation() {
        // GIVEN
        val contactName = "Test User"
        val scenario = ActivityScenario.launchActivity<AddContactActivity>()

        // WHEN
        // Enter contact name
        onView(withId(R.id.contact_name_text))
            .perform(typeText(contactName))
        // Destroy and recreate Activity
        scenario.recreate()

        // THEN
        // Check contact name was preserved.
        onView(withId(R.id.contact_name_text))
            .check(matches(withText(contactName)))
     }
}

Most UI tests don't interact with the framework and you can run them on Robolectric. You can run behavior tests on Robolectric as the fidelity needed for it is more than enough. For example, when a Compose test verifies that the UI has changed after clicking a button.

You can run other UI tests with Robolectric, such as screenshot tests. However the fidelity is lower as different devices render screens slightly differently.

You must decide whether Robolectric's implementation is good enough for each use case, but here are some recommendations:

  • Use Robolectric for isolated UI behavior tests for components, feature or application tests. In general these tests check the state management and behavior of the UIs and don't interact with external dependencies.
  • Use Robolectric to take screenshots when the pixel accuracy is not critical. For example, to test how a component reacts to different font sizes or themes.

Note: Robolectric can take screenshots natively, but you need a third-party library to perform screenshot testing with it.

Robolectric versus device tests

In summary, Robolectric provides enough fidelity to run most UI tests but some cases will still require device tests, for example those related to system UI like edge-to-edge or picture-in-picture, or when relying on unsupported features, like WebView.