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
.