ComposeUiTest

Interface

Common
@ExperimentalTestApi
expect sealed interface ComposeUiTest : SemanticsNodeInteractionsProvider

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.

Properties

Common
val density: Density

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.

Common
val mainClock: MainTestClock

Clock that drives frames and recompositions in compose tests.

Functions

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

Runs the given action on the UI thread.

This method blocks until the action is complete.

fun <T> 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.

fun waitForIdle()

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.

suspend fun awaitIdle()

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.

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

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

conditionDescriptionAn optional human-readable description of condition that will be included in the timeout exception if thrown.
timeoutMillisThe time after which this method throws an exception if the given condition is not satisfied. This observes wall clock time, not test clock time.
conditionCondition that must be satisfied in order for this method to successfully finish.
fun setContent(composable: @Composable () -> Unit)

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

Android
@ExperimentalTestApi
actual sealed interface ComposeUiTest : SemanticsNodeInteractionsProvider

Properties

Android
actual val density: Density
Android
actual val mainClock: MainTestClock

Functions

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun setComposeAccessibilityValidator(validator: ComposeAccessibilityValidator?)

Sets the ComposeAccessibilityValidator to perform the accessibility checks with. Providing null means disabling the accessibility checks

actual fun <T> runOnUiThread(action: () -> T): T
actual fun <T> runOnIdle(action: () -> T): T
actual fun waitForIdle()
actual suspend fun awaitIdle()
actual fun waitUntil(
        conditionDescription: String?,
        timeoutMillis: Long,
        condition: () -> Boolean,
    )
fun registerIdlingResource(idlingResource: IdlingResource)

Registers an IdlingResource in this test.

fun unregisterIdlingResource(idlingResource: IdlingResource)

Unregisters an IdlingResource from this test.

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