jvmMain.androidx.compose.ui.test.junit4.ComposeTestRule.jvm.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ui-test-junit4-desktop Show documentation
Show all versions of ui-test-junit4-desktop Show documentation
Compose testing integration with JUnit4
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.test.junit4
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.ComposeTimeoutException
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.IdlingResource
import androidx.compose.ui.test.MainTestClock
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import androidx.compose.ui.unit.Density
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import org.junit.rules.TestRule
/**
* A [TestRule] that allows you to test and control composables and applications using Compose.
* 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
* animation 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][mainClock]. You'll have to disable [automatic advancing][MainTestClock.autoAdvance],
* and manually advance the clock by the time necessary to position the button in the middle of
* the screen.
*
* An instance of [ComposeTestRule] can be created with [createComposeRule], which will also
* create a host for the compose content for you (see [ComposeContentTestRule]). If you need to
* specify which particular Activity is started on Android, you can use [createAndroidComposeRule].
*
* If you don't want any Activity to be started automatically by the test rule on Android, you
* can use [createEmptyComposeRule]. In such a case, you will have to set content using one of
* Compose UI's setters (like [ComponentActivity.setContent][androidx.compose.ui.platform
* .setContent]).
*/
@JvmDefaultWithCompatibility
interface ComposeTestRule : TestRule, SemanticsNodeInteractionsProvider {
/**
* Current device screen's density.
*/
val density: Density
/**
* Clock that drives frames and recompositions in compose tests.
*/
val mainClock: MainTestClock
/**
* Runs the given action on the UI thread.
*
* This method is blocking until the action is complete.
*/
fun runOnUiThread(action: () -> T): T
/**
* Executes the given action in the same way as [runOnUiThread] but also makes sure Compose
* is idle before executing it. This is great place for doing your assertions on shared
* variables.
*
* This method is blocking until the action is complete.
*
* In case the main clock auto advancement is enabled (by default is) this will also keep
* advancing the clock until it is idle (meaning there are no recompositions, animations, etc.
* pending). If not, this will wait only for other idling resources.
*/
fun runOnIdle(action: () -> T): T
/**
* Waits for compose to be idle.
*
* This is a blocking call. Returns only after compose is idle.
*
* In case the main clock auto advancement is enabled (by default is) this will also keep
* advancing the clock until it is idle (meaning there are no recompositions, animations, etc.
* pending). If not, this will wait only for other idling resources.
*
* Can crash in case there is a time out. This is not supposed to be handled as it
* surfaces only in incorrect tests.
*/
fun waitForIdle()
/**
* Suspends until compose is idle. Compose is idle if there are no pending compositions, no
* pending changes that could lead to another composition, and no pending draw calls.
*
* In case the main clock auto advancement is enabled (by default is) this will also keep
* advancing the clock until it is idle (meaning there are no recompositions, animations, etc.
* pending). If not, this will wait only for other idling resources.
*/
suspend fun awaitIdle()
/**
* Blocks until the given condition is satisfied.
*
* In case the main clock auto advancement is enabled (by default is), this will also keep
* advancing the clock on a frame by frame basis and yield for other async work at the end of
* each frame. If the advancement of the main clock is not enabled this will work as a
* countdown latch without any other advancements.
*
* There is also [MainTestClock.advanceTimeUntil] which is faster as it does not yield back
* the UI thread.
*
* This method should be used in cases where [MainTestClock.advanceTimeUntil]
* is not enough.
*
* @param timeoutMillis The time after which this method throws an exception if the given
* condition is not satisfied. This is the wall clock time not the main clock one.
* @param condition 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].
*/
fun waitUntil(timeoutMillis: Long = 1_000, condition: () -> Boolean)
/**
* Blocks until the number of nodes matching the given [matcher] is equal to the given [count].
*
* @see ComposeTestRule.waitUntil
*
* @param matcher The matcher that will be used to filter nodes.
* @param count The number of nodes that are expected to
* @param timeoutMillis 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).
*/
@ExperimentalTestApi
fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeoutMillis: Long = 1_000L)
/**
* Blocks until at least one node matches the given [matcher].
*
* @see ComposeTestRule.waitUntil
*
* @param matcher The matcher that will be used to filter nodes.
* @param timeoutMillis 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).
*/
@ExperimentalTestApi
fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeoutMillis: Long = 1_000L)
/**
* Blocks until exactly one node matches the given [matcher].
*
* @see ComposeTestRule.waitUntil
*
* @param matcher The matcher that will be used to filter nodes.
* @param timeoutMillis 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).
*/
@ExperimentalTestApi
fun waitUntilExactlyOneExists(matcher: SemanticsMatcher, timeoutMillis: Long = 1_000L)
/**
* Blocks until no nodes match the given [matcher].
*
* @see ComposeTestRule.waitUntil
*
* @param matcher The matcher that will be used to filter nodes.
* @param timeoutMillis 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).
*/
@ExperimentalTestApi
fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeoutMillis: Long = 1_000L)
/**
* Registers an [IdlingResource] in this test.
*/
fun registerIdlingResource(idlingResource: IdlingResource)
/**
* Unregisters an [IdlingResource] from this test.
*/
fun unregisterIdlingResource(idlingResource: IdlingResource)
}
/**
* A [ComposeTestRule] that allows you to set content without the necessity to provide a host for
* the content. The host, such as an Activity, will be created by the test rule.
*
* An instance of [ComposeContentTestRule] can be created with [createComposeRule]. If you need to
* specify which particular Activity is started on Android, you can use [createAndroidComposeRule].
*
* If you don't want any host to be started automatically by the test rule on Android, you
* can use [createEmptyComposeRule]. In such a case, you will have to create a host in your test
* and set the content using one of Compose UI's setters (like [ComponentActivity
* .setContent][androidx.activity.compose.setContent]).
*/
@JvmDefaultWithCompatibility
interface ComposeContentTestRule : ComposeTestRule {
/**
* Sets the given composable as a content of the current screen.
*
* Use this in your tests to setup the UI content to be tested. This should be called exactly
* once per test.
*
* @throws IllegalStateException if called more than once per test.
*/
fun setContent(composable: @Composable () -> Unit)
}
/**
* Factory method to provide an implementation of [ComposeContentTestRule].
*
* This method is useful for tests in compose libraries where it is irrelevant where the compose
* content is hosted (e.g. an Activity on Android). Such tests typically set compose content
* themselves via [setContent][ComposeContentTestRule.setContent] and only instrument and assert
* that content.
*
* For Android this will use the default Activity (android.app.Activity). You need to add a
* reference to this activity into the manifest file of the corresponding tests (usually in
* androidTest/AndroidManifest.xml). If your Android test requires a specific Activity to be
* launched, see [createAndroidComposeRule].
*/
expect fun createComposeRule(): ComposeContentTestRule
/**
* Factory method to provide an implementation of [ComposeContentTestRule].
*
* This method is useful for tests in compose libraries where it is irrelevant where the compose
* content is hosted (e.g. an Activity on Android). Such tests typically set compose content
* themselves via [setContent][ComposeContentTestRule.setContent] and only instrument and assert
* that content.
*
* For Android this will use the default Activity (android.app.Activity). You need to add a
* reference to this activity into the manifest file of the corresponding tests (usually in
* androidTest/AndroidManifest.xml). If your Android test requires a specific Activity to be
* launched, see [createAndroidComposeRule].
*
* @param effectContext The [CoroutineContext] used to run the composition. The context for
* `LaunchedEffect`s and `rememberCoroutineScope` will be derived from this context.
*/
@ExperimentalTestApi
expect fun createComposeRule(
effectContext: CoroutineContext = EmptyCoroutineContext
): ComposeContentTestRule
© 2015 - 2025 Weber Informatics LLC | Privacy Policy