ComposeUiTest
@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
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
.
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 IdlingResource
s 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 IdlingResource
s 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 IdlingResource
s 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 IdlingResource
s 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
conditionDescription | An optional human-readable description of condition that will be included in the timeout exception if thrown. |
timeoutMillis | 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 | Condition 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.
@ExperimentalTestApi
actual sealed interface ComposeUiTest : SemanticsNodeInteractionsProvider
Properties
actual val density: Density
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)