Robolectric

The previous two lessons left a gap: plain JUnit unit tests can't touch real Android framework classes, and Espresso needs an actual device or emulator for every run. Robolectric fills that gap — it simulates the Android framework well enough to run code that uses Activity, Context, and View directly on the local JVM, with no device attached at all.

Adding Robolectric

// app/build.gradle
dependencies {
    testImplementation 'org.robolectric:robolectric:4.13'
    testImplementation 'androidx.test:core:1.6.1'
}
Explanation: Notice these are testImplementation, not androidTestImplementation — Robolectric tests live in src/test/java alongside the plain JUnit tests from earlier, because they run on the JVM too. Only the test runner is different.

@RunWith(RobolectricTestRunner.class)

Annotating a test class with @RunWith(RobolectricTestRunner.class) tells JUnit to let Robolectric intercept and simulate Android framework calls for every test in that class, instead of running them unmodified on the plain JVM (which would throw "not mocked" errors, as noted in the Unit Testing lesson).

Worked Example: Testing an Activity

This test launches RecipeBox's RecipeDetailActivity with Robolectric and checks that a recipe's title is actually rendered in its TextView — real Activity lifecycle and real view inflation, without a device.

@RunWith(RobolectricTestRunner.class)
public class RecipeDetailActivityTest {

    @Test
    public void displaysRecipeTitle() {
        Intent intent = new Intent(ApplicationProvider.getApplicationContext(), RecipeDetailActivity.class);
        intent.putExtra("recipeId", "recipe_42");

        RecipeDetailActivity activity = Robolectric.buildActivity(RecipeDetailActivity.class, intent)
                .create()
                .resume()
                .get();

        TextView titleView = activity.findViewById(R.id.tvRecipeTitle);

        assertEquals("Pancakes", titleView.getText().toString());
    }
}
Explanation:
  • Robolectric.buildActivity(...) drives the real Activity lifecycle methods (create(), resume(), etc.) exactly as Android would, but simulated on the JVM.
  • activity.findViewById(...) works because Robolectric actually inflates the real layout XML — this is not a mock of the view, it's the genuine inflated TextView.
  • This runs in a fraction of a second compared to an equivalent Espresso test, since there's no APK install or device boot involved.

Robolectric vs Espresso vs Plain JUnit

Plain JUnitRobolectricEspresso (instrumented)
Runs onJVMJVM (simulated Android)Real device/emulator
Android framework classesNot availableSimulatedReal
SpeedFastestFastSlowest
Best forPure logic (validators, mappers)Activities/Fragments without a real deviceFull UI flows, real rendering
Confidence levelLogic onlyHigh, but simulatedHighest — the real thing
A common strategy: write plain JUnit tests for RecipeBox's Repository and validators, Robolectric tests for Activities/Fragments that need Android classes but don't need pixel-perfect real rendering, and a smaller set of Espresso tests for the handful of flows that must be verified on a genuine device.
With three layers of test coverage in place, the next lesson turns to a different concern — Profiling & Performance, making sure RecipeBox not only works correctly but runs smoothly too.