Skip to content

Test UI

Ali Sadeghi edited this page Feb 5, 2026 · 7 revisions

Test UI Agent

Generates Compose UI tests using runComposeUiTest.

Spawned by: Feature-Test command

Key Concept: ScreenRoot Pattern

Tests ALWAYS target {Feature}ScreenRoot, not {Feature}Screen.

ScreenRoot is ViewModel-independent and takes UiState + callbacks directly, making it testable without mocking.

// Screen - ViewModel wrapper (NOT TESTED DIRECTLY)
@Composable
fun LoginScreen(viewModel: LoginViewModel = koinViewModel(), ...)

// ScreenRoot - ViewModel-independent (THIS IS TESTED)
@Composable
fun LoginScreenRoot(uiState: LoginUiModel, onLogin: () -> Unit, onRetry: () -> Unit, ...)

What It Tests

  • State rendering (all 4 states: Uninitialized, Loading, Success, Failed)
  • User interactions (button clicks, text input)
  • Callback invocations (onRetry, onBackClick, onItemClick)
  • Accessibility (content descriptions)
  • Error message display using actual ErrorConst messages

Example Output

@OptIn(ExperimentalTestApi::class)
class LoginScreenTest {

    @Test
    fun `shows loading indicator when state is Loading`() = runComposeUiTest {
        setContent {
            MaterialTheme {
                LoginScreenRoot(
                    uiState = LoginUiFixtures.createLoadingState(),
                    onLogin = {},
                    onRetry = {},
                    onBackClick = {}
                )
            }
        }

        onNodeWithContentDescription("Loading").assertExists()
    }

    @Test
    fun `shows network error message and retry button`() = runComposeUiTest {
        setContent {
            LoginScreenRoot(
                uiState = LoginUiFixtures.createNetworkErrorState(),
                onLogin = {},
                onRetry = {},
                onBackClick = {}
            )
        }

        // ErrorConst.NoNetwork message
        onNodeWithText("Error, Check your connection and try again.", substring = true).assertIsDisplayed()
        onNodeWithText("Retry").assertIsDisplayed()
    }

    @Test
    fun `retry button invokes onRetry callback`() = runComposeUiTest {
        var retryCalled = false

        setContent {
            LoginScreenRoot(
                uiState = LoginUiFixtures.createNetworkErrorState(),
                onLogin = {},
                onRetry = { retryCalled = true },
                onBackClick = {}
            )
        }

        onNodeWithText("Retry").performClick()
        assertTrue(retryCalled)
    }

    @Test
    fun `shows content when state is Success`() = runComposeUiTest {
        setContent {
            MaterialTheme {
                LoginScreenRoot(
                    uiState = LoginUiFixtures.createSuccessState(),
                    onLogin = {},
                    onRetry = {},
                    onBackClick = {}
                )
            }
        }

        onNodeWithText("Welcome").assertIsDisplayed()
    }
}

UiFixtures Usage

All UI tests use {Feature}UiFixtures for consistent state creation:

// Create specific states for testing
LoginUiFixtures.createLoadingState()
LoginUiFixtures.createSuccessState()
LoginUiFixtures.createNetworkErrorState()  // Uses ErrorConst message
LoginUiFixtures.createUnauthorizedErrorState()

Back to Testing-Agents | Agents

Clone this wiki locally