DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Compose UI Testing — Test-Driven Reliable UI Development

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()
}
Enter fullscreen mode Exit fullscreen mode

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")
)
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

Compose testing makes UI reliability achievable and maintainable.

8 Android app templates: Gumroad

Top comments (0)