UI Testing & Integration Testing
📖 Concept
UI testing validates the app's behavior from the user's perspective. At the senior level, you should design a testing strategy that balances speed and coverage.
The testing pyramid:
/ E2E Tests \ Few, slow, high confidence
/ UI Tests \ Moderate count
/ Integration \ Moderate count
/ Unit Tests \ Many, fast, low cost
UI testing frameworks:
- Compose Testing: Built-in test rules for Compose UIs. Find by semantics, perform actions, assert state.
- Espresso: For View-based UIs. Find by ID/text, perform actions, check assertions.
- UI Automator: Tests across apps (system UI, notifications, permissions).
- Robolectric: Run Android tests on JVM without emulator. Very fast.
Screenshot testing: Compare rendered UI against reference screenshots. Catches visual regressions. Libraries: Paparazzi, Roborazzi, Compose Preview Screenshot Testing.
Integration testing: Tests multiple components working together (ViewModel + Repository + FakeDataSource). Runs on device/emulator. Uses Hilt's @TestInstallIn to replace production modules.
💻 Code Example
1// Compose UI test2class TaskListScreenTest {3 @get:Rule4 val composeRule = createComposeRule()56 @Test7 fun displaysTasks_whenLoaded() {8 val tasks = listOf(9 Task("1", "Buy groceries", false),10 Task("2", "Write tests", true)11 )1213 composeRule.setContent {14 TaskListScreen(15 state = TaskUiState.Success(tasks),16 onToggle = {},17 onDelete = {}18 )19 }2021 // Assert tasks are displayed22 composeRule.onNodeWithText("Buy groceries").assertIsDisplayed()23 composeRule.onNodeWithText("Write tests").assertIsDisplayed()2425 // Assert completed task has different visual26 composeRule.onNodeWithText("Write tests")27 .assertHasClickAction()28 }2930 @Test31 fun showsLoadingIndicator_whenLoading() {32 composeRule.setContent {33 TaskListScreen(34 state = TaskUiState.Loading,35 onToggle = {},36 onDelete = {}37 )38 }3940 composeRule.onNodeWithContentDescription("Loading")41 .assertIsDisplayed()42 }4344 @Test45 fun callsOnToggle_whenTaskClicked() {46 var toggledId: String? = null4748 composeRule.setContent {49 TaskListScreen(50 state = TaskUiState.Success(listOf(Task("1", "Test", false))),51 onToggle = { toggledId = it },52 onDelete = {}53 )54 }5556 composeRule.onNodeWithText("Test").performClick()57 assertEquals("1", toggledId)58 }59}6061// Integration test with Hilt62@HiltAndroidTest63@RunWith(AndroidJUnit4::class)64class TaskFeatureTest {65 @get:Rule(order = 0)66 val hiltRule = HiltAndroidRule(this)6768 @get:Rule(order = 1)69 val composeRule = createAndroidComposeRule<MainActivity>()7071 @BindValue // Replace real repository with fake72 val repository: TaskRepository = FakeTaskRepository()7374 @Before75 fun setup() {76 hiltRule.inject()77 }7879 @Test80 fun createAndCompleteTask_endToEnd() {81 // Add a task82 composeRule.onNodeWithContentDescription("Add Task").performClick()83 composeRule.onNodeWithTag("task_input").performTextInput("New task")84 composeRule.onNodeWithText("Save").performClick()8586 // Verify it appears87 composeRule.onNodeWithText("New task").assertIsDisplayed()8889 // Toggle complete90 composeRule.onNodeWithText("New task").performClick()9192 // Verify completion state93 composeRule.onNodeWithText("New task")94 .assertExists() // Still visible but marked complete95 }96}
🏋️ Practice Exercise
Practice:
- Write Compose UI tests for a login screen (email validation, button state, error display)
- Create a screenshot test using Paparazzi for a card component
- Write an integration test that uses Hilt's @BindValue to inject fakes
- Test navigation between two screens using the Navigation test artifact
- Set up Robolectric for fast JVM-based UI tests
⚠️ Common Mistakes
Testing UI implementation details (view IDs) instead of user-visible behavior (text, content descriptions)
Not adding testTags/semantics to Compose components — makes testing impossible without them
Running all tests as instrumented tests — use Robolectric for fast feedback loop
Not testing error states — only testing the happy path misses the most common UX issues
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for UI Testing & Integration Testing. Login to unlock this feature.