ComposeUiTest

Known direct subclasses
AndroidComposeUiTest

Variant of ComposeUiTest for when you want to have access the current activity of type A.


A test environment that allows you to test and control composables, either in isolation or in applications. Most of the functionality in this interface provides some form of test synchronization: the test will block until the app or composable is idle, to ensure the tests are deterministic.

For example, if you would perform a click on the center of the screen while a button is animating from left to right over the screen, without synchronization the test would sometimes click when the button is in the middle of the screen (button is clicked), and sometimes when the button is past the middle of the screen (button is not clicked). With synchronization, the app would not be idle until the animation is over, so the test will always click when the button is past the middle of the screen (and not click it). If you actually do want to click the button when it's in the middle of the animation, you can do so by controlling the clock. You'll have to disable automatic advancing, and manually advance the clock by the time necessary to position the button in the middle of the screen.

To test a composable in isolation, use setContent to set the composable in a host. On Android, the host is an Activity. When using runComposeUiTest or any of its platform specific variants, the host will be started for you automatically, unless otherwise specified. To test an application, use the platform specific variant of runComposeUiTest that launches the app.

An instance of ComposeUiTest can be obtained through runComposeUiTest or any of its platform specific variants, the argument to which will have it as the receiver scope.

Summary

Public functions

suspend Unit

Suspends until the UI is idle.

Cmn
android
Unit

Disables accessibility checks.

Cmn
android
Unit

Enables accessibility checks that will be run before every action that is expected to change the UI.

Cmn
android
Unit

Registers an IdlingResource in this test.

Cmn
android
T
<T : Any?> runOnIdle(action: () -> T)

Executes the given action in the same way as runOnUiThread but waits until the app is idle before executing the action.

Cmn
android
T
<T : Any?> runOnUiThread(action: () -> T)

Runs the given action on the UI thread.

Cmn
android
Unit
setContent(composable: @Composable () -> Unit)

Sets the given composable as the content to be tested.

Cmn
android
Unit

Unregisters an IdlingResource from this test.

Cmn
android
Unit

Waits for the UI to become idle.

Cmn
android
Unit
waitUntil(
    conditionDescription: String?,
    timeoutMillis: Long,
    condition: () -> Boolean
)

Blocks until the given condition is satisfied.

Cmn
android

Public properties

AccessibilityValidator?

The AccessibilityValidator that will be used to run Android accessibility checks before every action that is expected to change the UI.

android
Density

Current device screen's density.

Cmn
android
MainTestClock

Clock that drives frames and recompositions in compose tests.

Cmn
android

Extension functions

Unit

Blocks until at least one node matches the given matcher.

Cmn
Unit

Blocks until no nodes match the given matcher.

Cmn
Unit

Blocks until exactly one node matches the given matcher.

Cmn
Unit
@ExperimentalTestApi
ComposeUiTest.waitUntilNodeCount(
    matcher: SemanticsMatcher,
    count: Int,
    timeoutMillis: Long
)

Blocks until the number of nodes matching the given matcher is equal to the given count.

Cmn

Inherited functions

From androidx.compose.ui.test.SemanticsNodeInteractionsProvider
SemanticsNodeInteractionCollection
onAllNodes(matcher: SemanticsMatcher, useUnmergedTree: Boolean)

Finds all semantics nodes that match the given condition.

android
SemanticsNodeInteractionCollection
onAllNodes(matcher: SemanticsMatcher, useUnmergedTree: Boolean)

Finds all semantics nodes that match the given condition.

Cmn
SemanticsNodeInteraction
onNode(matcher: SemanticsMatcher, useUnmergedTree: Boolean)

Finds a semantics node that matches the given condition.

android
SemanticsNodeInteraction
onNode(matcher: SemanticsMatcher, useUnmergedTree: Boolean)

Finds a semantics node that matches the given condition.

Cmn

Public functions

awaitIdle

suspend fun awaitIdle(): Unit

Suspends until the UI is idle. Quiescence is reached when there are no more pending changes (e.g. pending recompositions or a pending draw call) and all IdlingResources are idle.

If auto advancement is enabled on the mainClock, this method will advance the clock to process any pending composition, invalidation and animation. If auto advancement is not enabled, the clock will not be advanced which means that the Compose UI appears to be frozen. This is ideal for testing animations in a deterministic way. This method will always wait for all IdlingResources to become idle.

Note that some processes are driven by the host operating system and will therefore still execute when auto advancement is disabled. For example, Android's measure, layout and draw passes can still happen if required by the View system.

disableAccessibilityChecks

@RequiresApi(value = 34)
fun disableAccessibilityChecks(): Unit

Disables accessibility checks.

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToIndex
import androidx.compose.ui.test.runComposeUiTest
import androidx.compose.ui.test.tryPerformAccessibilityChecks

@Test
@OptIn(ExperimentalTestApi::class)
fun testWithAccessibilityChecks() = runComposeUiTest {
    // Enable accessibility checks with default configuration:
    enableAccessibilityChecks()

    // When enabled, accessibility checks run automatically when performing an action:
    onNodeWithText("Submit").performClick()

    // You can also manually run accessibility checks:
    onRoot().tryPerformAccessibilityChecks()

    // When disabling accessibility checks..
    disableAccessibilityChecks()

    // .. they no longer run when performing an action:
    onNodeWithTag("list").performScrollToIndex(15)
}

enableAccessibilityChecks

@RequiresApi(value = 34)
fun enableAccessibilityChecks(): Unit

Enables accessibility checks that will be run before every action that is expected to change the UI.

Accessibility checks are platform dependent, refer to the documentation of the platform specific variant of ComposeUiTest to see if it is supported and how you can configure it.

On Android, this requires API 34+ (Android U), and currently does not work on Robolectric.

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToIndex
import androidx.compose.ui.test.runComposeUiTest
import androidx.compose.ui.test.tryPerformAccessibilityChecks

@Test
@OptIn(ExperimentalTestApi::class)
fun testWithAccessibilityChecks() = runComposeUiTest {
    // Enable accessibility checks with default configuration:
    enableAccessibilityChecks()

    // When enabled, accessibility checks run automatically when performing an action:
    onNodeWithText("Submit").performClick()

    // You can also manually run accessibility checks:
    onRoot().tryPerformAccessibilityChecks()

    // When disabling accessibility checks..
    disableAccessibilityChecks()

    // .. they no longer run when performing an action:
    onNodeWithTag("list").performScrollToIndex(15)
}

registerIdlingResource

fun registerIdlingResource(idlingResource: IdlingResource): Unit

Registers an IdlingResource in this test.

runOnIdle

fun <T : Any?> runOnIdle(action: () -> T): T

Executes the given action in the same way as runOnUiThread but waits until the app is idle before executing the action. This is the recommended way of doing your assertions on shared variables.

This method blocks until the action is complete.

runOnUiThread

fun <T : Any?> runOnUiThread(action: () -> T): T

Runs the given action on the UI thread.

This method blocks until the action is complete.

setContent

fun setContent(composable: @Composable () -> Unit): Unit

Sets the given composable as the content to be tested. This should be called exactly once per test.

Throws
kotlin.IllegalStateException

if called more than once per test, or if the implementation doesn't have access to a host to set content in.

unregisterIdlingResource

fun unregisterIdlingResource(idlingResource: IdlingResource): Unit

Unregisters an IdlingResource from this test.

waitForIdle

fun waitForIdle(): Unit

Waits for the UI to become idle. Quiescence is reached when there are no more pending changes (e.g. pending recompositions or a pending draw call) and all IdlingResources are idle.

If auto advancement is enabled on the mainClock, this method will advance the clock to process any pending composition, invalidation and animation. If auto advancement is not enabled, the clock will not be advanced which means that the Compose UI appears to be frozen. This is ideal for testing animations in a deterministic way. This method will always wait for all IdlingResources to become idle.

Note that some processes are driven by the host operating system and will therefore still execute when auto advancement is disabled. For example, Android's measure, layout and draw passes can still happen if required by the View system.

waitUntil

fun waitUntil(
    conditionDescription: String? = null,
    timeoutMillis: Long,
    condition: () -> Boolean
): Unit

Blocks until the given condition is satisfied.

If auto advancement is enabled on the mainClock, this method will actively advance the clock to process any pending composition, invalidation and animation. If auto advancement is not enabled, the clock will not be advanced actively which means that the Compose UI appears to be frozen. It is still valid to use this method in this way, if the condition will be satisfied by something not driven by our clock.

Compared to MainTestClock.advanceTimeUntil, waitUntil sleeps after every iteration to yield to other processes. This gives waitUntil a better integration with the host, but it is less preferred from a performance viewpoint. Therefore, we recommend that you try using MainTestClock.advanceTimeUntil before resorting to waitUntil.

Parameters
conditionDescription: String? = null

An optional human-readable description of condition that will be included in the timeout exception if thrown.

timeoutMillis: Long

The time after which this method throws an exception if the given condition is not satisfied. This observes wall clock time, not test clock time.

condition: () -> Boolean

Condition that must be satisfied in order for this method to successfully finish.

Throws
androidx.compose.ui.test.ComposeTimeoutException

If the condition is not satisfied after timeoutMillis (in wall clock time).

Public properties

accessibilityValidator

var accessibilityValidatorAccessibilityValidator?

The AccessibilityValidator that will be used to run Android accessibility checks before every action that is expected to change the UI.

If no validator is set (null), no checks will be performed. You can either supply your own validator directly, or have one configured for you with enableAccessibilityChecks.

The default value is null.

This requires API 34+ (Android U), and currently does not work on Robolectric.

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.runAndroidComposeUiTest

@Test
@OptIn(ExperimentalTestApi::class)
fun testWithAccessibilityChecks() =
    runAndroidComposeUiTest<ComponentActivity> {
        // Enable accessibility checks with your own AccessibilityValidator:
        accessibilityValidator = AccessibilityValidator()
        // By setting a non-null AccessibilityValidator, accessibility checks are enabled

        // Configure the AccessibilityValidator:
        @OptIn(ExperimentalTestApi::class)
        accessibilityValidator!!.setThrowExceptionFor(AccessibilityCheckResultType.ERROR)
    }

density

val densityDensity

Current device screen's density. Note that it is technically possible for a Compose hierarchy to define a different density for a certain subtree. Try to use LayoutInfo.density where possible, which can be obtained from SemanticsNode.layoutInfo.

mainClock

val mainClockMainTestClock

Clock that drives frames and recompositions in compose tests.

Extension functions

waitUntilAtLeastOneExists

@ExperimentalTestApi
fun ComposeUiTest.waitUntilAtLeastOneExists(
    matcher: SemanticsMatcher,
    timeoutMillis: Long
): Unit

Blocks until at least one node matches the given matcher.

Parameters
matcher: SemanticsMatcher

The matcher that will be used to filter nodes.

timeoutMillis: Long

The time after which this method throws an exception if no nodes match the given matcher. This observes wall clock time, not frame time.

Throws
androidx.compose.ui.test.ComposeTimeoutException

If no nodes match the given matcher after timeoutMillis (in wall clock time).

See also
waitUntil

waitUntilDoesNotExist

@ExperimentalTestApi
fun ComposeUiTest.waitUntilDoesNotExist(
    matcher: SemanticsMatcher,
    timeoutMillis: Long
): Unit

Blocks until no nodes match the given matcher.

Parameters
matcher: SemanticsMatcher

The matcher that will be used to filter nodes.

timeoutMillis: Long

The time after which this method throws an exception if any nodes match the given matcher. This observes wall clock time, not frame time.

Throws
androidx.compose.ui.test.ComposeTimeoutException

If any nodes match the given matcher after timeoutMillis (in wall clock time).

See also
waitUntil

waitUntilExactlyOneExists

@ExperimentalTestApi
fun ComposeUiTest.waitUntilExactlyOneExists(
    matcher: SemanticsMatcher,
    timeoutMillis: Long
): Unit

Blocks until exactly one node matches the given matcher.

Parameters
matcher: SemanticsMatcher

The matcher that will be used to filter nodes.

timeoutMillis: Long

The time after which this method throws an exception if exactly one node does not match the given matcher. This observes wall clock time, not frame time.

Throws
androidx.compose.ui.test.ComposeTimeoutException

If exactly one node does not match the given matcher after timeoutMillis (in wall clock time).

See also
waitUntil

waitUntilNodeCount

@ExperimentalTestApi
fun ComposeUiTest.waitUntilNodeCount(
    matcher: SemanticsMatcher,
    count: Int,
    timeoutMillis: Long
): Unit

Blocks until the number of nodes matching the given matcher is equal to the given count.

Parameters
matcher: SemanticsMatcher

The matcher that will be used to filter nodes.

count: Int

The number of nodes that are expected to be matched.

timeoutMillis: Long

The time after which this method throws an exception if the number of nodes that match the matcher is not count. This observes wall clock time, not frame time.

Throws
androidx.compose.ui.test.ComposeTimeoutException

If the number of nodes that match the matcher is not count after timeoutMillis (in wall clock time).

See also
waitUntil