AndroidX Test
Robolectric is intended to be fully compatible with Android's official testing libraries since version 4.0. As such, we encourage you to try these new APIs and provide feedback. At some point, the Robolectric equivalents will be deprecated and removed. Using the AndroidX Test APIs reduces the cognitive load for you as a developer, with just one set of APIs to learn for the same Android concept, no matter if you are writing an Robolectric test or an instrumentation test. Furthermore, it will make your tests more portable and compatible with our future plans.
TestRunner
It is now possible to use the AndroidX test runner in Robolectric tests. If you require a custom test runner, please check out the configuration and plugin API, and let us know if there are any extension points missing that you require.
Robolectric
AndroidX Test
Application
Since most Android code is centric around a Context
, getting hold of your
application’s context is a typical task for most tests.
Robolectric
AndroidX Test
Activities
Robolectric provides Robolectric.setupActivity()
for the
coarse-grained use case where you require a launched activity in the resumed state and visible for
the user to interact with.
Robolectric also provides Robolectric.buildActivity()
, which returns
an ActivityController
that allows the developer to step through the
Activity
lifecycle. This has proved problematic as it requires
developers to fully understand valid lifecycle transitions and possible valid states. Using an
Activity
in an invalid state has undefined behavior and can cause compatibility issues when
running on different Android test runtimes or when upgrading to newer versions of Robolectric.
ActivityScenario
provides a replacement for both of these use cases, but
places tighter restrictions around lifecycle transitions, namely that invalid or incomplete
transitions are not possible. If you'd like a Rule
-based equivalent please use
ActivityScenarioRule
instead.
Robolectric
import org.robolectric.Robolectric;
import org.robolectric.android.controller.ActivityController;
public class LocationTrackerActivityTest {
@Test
public void locationListenerShouldBeUnregisteredInCreatedState() {
// GIVEN
ActivityController<LocationTrackerActivity> controller = Robolectric.buildActivity<LocationTrackerActivity>().setup();
// WHEN
controller.pause().stop();
// THEN
assertThat(controller.get().getLocationListener()).isNull();
}
}
import org.robolectric.Robolectric
class LocationTrackerActivityTest {
@Test
fun locationListenerShouldBeUnregisteredInCreatedState() {
// GIVEN
val controller = Robolectric.buildActivity<LocationTrackerActivity>().setup()
// WHEN
controller.pause().stop()
// THEN
assertThat(controller.get().locationListener).isNull()
}
}
AndroidX Test
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
public class LocationTrackerActivityTest {
@Test
public void locationListenerShouldBeUnregisteredInCreatedState() {
// GIVEN
ActivityScenario<LocationTrackerActivity> scenario = ActivityScenario.launchActivity<LocationTrackerActivity>();
// WHEN
scenario.moveToState(Lifecycle.State.CREATED);
// THEN
scenario.onActivity(activity -> assertThat(activity.getLocationListener()).isNull());
}
}
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
class LocationTrackerActivityTest {
@Test
fun locationListenerShouldBeUnregisteredInCreatedState() {
// GIVEN
val scenario = ActivityScenario.launchActivity<LocationTrackerActivity>()
// WHEN
scenario.moveToState(Lifecycle.State.CREATED)
// THEN
scenario.onActivity { activity ->
assertThat(activity.locationListener).isNull()
}
}
}
Note that in Robolectric since both the test and UI event loop run on the same thread,
synchronization is not an issue. ActivityScenario.onActivity
provides a safe way of accessing the Activity
, should you need to, that will be guaranteed to be
compatible with our future plans.
Views
Robolectric has very limited APIs for View
interaction. In most cases, test
writers can just use Android APIs, such as Activity.findViewById()
which was safe since Robolectric tests do not have to worry about synchronization between test and
UI threads.
Espresso is the View
matching and interaction library of choice for instrumentation
tests. Since Robolectric 4.0, Espresso APIs are now supported in Robolectric tests.
import static androidx.test.espresso.Espresso.onView;
@RunWith(AndroidJUnit4.class)
public class AddContactActivityTest {
@Test
public void inputTextShouldBeRetainedAfterActivityRecreation() {
// GIVEN
String contactName = "Test User";
ActivityScenario<AddContactActivity> 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)));
}
}
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)))
}
}
Fragments
AndroidX Test provides FragmentScenario
, which offers APIs to safely create
your Fragment
under test and drive it through valid transitions.
import androidx.fragment.app.testing.FragmentScenario;
@RunWith(AndroidJUnit4.class)
public class FragmentTest {
@Test
public void testEventFragment() {
Bundle arguments = Bundle();
MyFragmentFactory factory = MyFragmentFactory();
FragmentScenario<MyFragment> scenario = FragmentScenario.launchFragmentInContainer<MyFragment>(arguments, factory);
onView(withId(R.id.text)).check(matches(withText("Hello World!")));
}
}
import androidx.fragment.app.testing.FragmentScenario
@RunWith(AndroidJUnit4::class)
class FragmentTest {
@Test
fun testEventFragment() {
val arguments = Bundle()
val factory = MyFragmentFactory()
val scenario = FragmentScenario.launchFragmentInContainer<MyFragment>(arguments, factory)
onView(withId(R.id.text)).check(matches(withText("Hello World!")))
}
}
Read more about testing Fragments here.