Compose UI Testing — Test-Driven Reliable UI Development
Compose provides a powerful testing framework for reliable UI development. Learn node finders, assertions, actions, and async testing.
Setup Compose Testing
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createComposeRule
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun testButtonClick() {
composeTestRule.setContent {
Button(onClick = { /* ... */ }) {
Text("Click Me")
}
}
composeTestRule.onNodeWithText("Click Me").assertIsDisplayed()
}
Find Nodes
// By text
composeTestRule.onNodeWithText("Submit", ignoreCase = true)
// By content description
composeTestRule.onNodeWithContentDescription("Close button")
// By test tag
composeTestRule.onNodeWithTag("submit_button")
// By multiple matchers
composeTestRule.onNode(
hasText("Delete") and hasContentDescription("Delete item")
)
Assertions
// Check visibility
composeTestRule.onNodeWithText("Loading").assertIsDisplayed()
composeTestRule.onNodeWithText("Error").assertDoesNotExist()
// Check enabled state
composeTestRule.onNodeWithTag("submit").assertIsEnabled()
composeTestRule.onNodeWithTag("submit").assertIsNotEnabled()
// Check properties
composeTestRule.onNodeWithText("Count: 5").assertTextEquals("Count: 5")
User Actions
// Click
composeTestRule.onNodeWithTag("button").performClick()
// Text input
composeTestRule.onNodeWithTag("input").performTextInput("Hello")
composeTestRule.onNodeWithTag("input").performTextClearance()
// Scrolling
composeTestRule.onNodeWithTag("list").performScrollToIndex(10)
// Swipe
composeTestRule.onNodeWithTag("item").performSwipeLeft()
List Testing
@Test
fun testListRendersItems() {
val items = listOf("Item 1", "Item 2", "Item 3")
composeTestRule.setContent {
LazyColumn {
items(items.size) {
Text(items[it])
}
}
}
items.forEach { item ->
composeTestRule.onNodeWithText(item).assertIsDisplayed()
}
}
@Test
fun testListScrolling() {
val items = (1..100).map { "Item $it" }
composeTestRule.setContent {
LazyColumn {
items(items.size) {
Text(items[it])
}
}
}
composeTestRule.onNodeWithText("Item 100").assertDoesNotExist()
composeTestRule.onNodeWithTag("list").performScrollToIndex(99)
composeTestRule.onNodeWithText("Item 100").assertIsDisplayed()
}
Async Testing with Clock Control
@Test
fun testDelayedContent() {
composeTestRule.setContent {
var showContent by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
delay(1000)
showContent = true
}
if (showContent) {
Text("Delayed Content")
}
}
composeTestRule.onNodeWithText("Delayed Content").assertDoesNotExist()
// Advance time without waiting
composeTestRule.mainClock.advanceTimeBy(1000)
composeTestRule.onNodeWithText("Delayed Content").assertIsDisplayed()
}
Full Example: Login Form Testing
@Test
fun testLoginForm() {
composeTestRule.setContent {
LoginScreen()
}
// Find and fill email field
composeTestRule
.onNodeWithTag("email_input")
.performTextInput("test@example.com")
// Find and fill password field
composeTestRule
.onNodeWithTag("password_input")
.performTextInput("password123")
// Click login button
composeTestRule.onNodeWithTag("login_button").performClick()
// Verify success message appears
composeTestRule.mainClock.advanceTimeBy(2000)
composeTestRule.onNodeWithText("Login successful").assertIsDisplayed()
}
Compose testing makes UI reliability achievable and maintainable.
8 Android app templates: Gumroad
Top comments (0)