UI Testing (Espresso)
The previous lesson's unit tests mocked away the Repository entirely. Espresso tests the opposite way: they drive the real RecipeBox app, running on a real device or emulator, by simulating an actual user tapping and typing on real screens.
Adding Espresso and the androidTest Source Set
// app/build.gradle
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
}
onView, withId, perform, check
Every Espresso test follows the same three-part shape: find a view, act on it, then assert something about the result.
Worked Example: Adding a Recipe
This test drives RecipeBox's add-recipe form exactly as a user would: type a title, type prep minutes, tap save, then confirm the new recipe shows up in the list.
@RunWith(AndroidJUnit4.class)
public class AddRecipeTest {
@Rule
public ActivityScenarioRule<AddRecipeActivity> activityRule =
new ActivityScenarioRule<>(AddRecipeActivity.class);
@Test
public void savingRecipe_showsItInList() {
onView(withId(R.id.etTitle))
.perform(typeText("Pancakes"), closeSoftKeyboard());
onView(withId(R.id.etPrepMinutes))
.perform(typeText("15"), closeSoftKeyboard());
onView(withId(R.id.btnSave))
.perform(click());
onView(withText("Pancakes"))
.check(matches(isDisplayed()));
}
}
- withId(R.id.etTitle) and withText("Pancakes") come from ViewMatchers; typeText(), click(), and closeSoftKeyboard() come from ViewActions — both are typically static-imported at the top of the test class.
- ActivityScenarioRule launches AddRecipeActivity fresh before this test and tears it down afterward, so tests don't interfere with each other.
- The final check(matches(isDisplayed())) confirms the saved recipe's title actually appears on screen — proving the whole save-and-refresh flow worked, not just that btnSave was clickable.
IdlingResource for Asynchronous Work
Espresso automatically waits for the main thread to go idle between steps, but it has no way to know about work happening elsewhere — such as RecipeBox's Repository saving to Room on a background thread before the list refreshes. Without help, the test can race ahead and check the list before the save finishes, causing flaky failures.
Espresso vs Unit Tests
| Question | Prefer |
|---|---|
| Is this validation/calculation logic correct? | Unit test (fast, no device) |
| Does tapping Save actually update the screen? | Espresso (real UI, real device) |
| Does the Repository call the right DAO method? | Unit test with Mockito |
| Does the whole add-recipe flow work end-to-end? | Espresso |