Skip to content

Commit 41228c3

Browse files
committed
Deprecate v1 test APIs in favor of v2, standardizing around StandardTestDispatcher
1 parent 69abcd7 commit 41228c3

7 files changed

Lines changed: 315 additions & 78 deletions

File tree

compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.desktop.kt

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,45 @@ import kotlin.coroutines.EmptyCoroutineContext
3737
import org.junit.runner.Description
3838
import org.junit.runners.model.Statement
3939

40-
@OptIn(InternalTestApi::class)
41-
actual fun createComposeRule(): ComposeContentTestRule = DesktopComposeTestRule()
42-
40+
@Deprecated(
41+
message =
42+
"Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use " +
43+
"`StandardTestDispatcher` by default to better simulate production behavior where " +
44+
"coroutines are queued rather than executed immediately.",
45+
level = DeprecationLevel.WARNING,
46+
)
47+
@OptIn(InternalTestApi::class, ExperimentalTestApi::class)
48+
actual fun createComposeRule(): ComposeContentTestRule =
49+
DesktopComposeTestRule(
50+
DesktopComposeUiTest(
51+
effectContext = EmptyCoroutineContext,
52+
useStandardTestDispatcherForComposition = false
53+
)
54+
)
55+
56+
@Deprecated(
57+
message =
58+
"Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use " +
59+
"`StandardTestDispatcher` by default to better simulate production behavior where " +
60+
"coroutines are queued rather than executed immediately.",
61+
level = DeprecationLevel.WARNING,
62+
)
4363
@ExperimentalTestApi
4464
@OptIn(InternalTestApi::class)
4565
actual fun createComposeRule(effectContext: CoroutineContext): ComposeContentTestRule =
46-
DesktopComposeTestRule(effectContext)
66+
DesktopComposeTestRule(
67+
DesktopComposeUiTest(
68+
effectContext = effectContext,
69+
useStandardTestDispatcherForComposition = false
70+
)
71+
)
4772

4873
@InternalTestApi
4974
@OptIn(ExperimentalTestApi::class)
50-
class DesktopComposeTestRule private constructor(
75+
class DesktopComposeTestRule internal constructor(
5176
private val composeTest: DesktopComposeUiTest
5277
) : ComposeContentTestRule {
5378

54-
constructor() : this(DesktopComposeUiTest())
55-
56-
@ExperimentalTestApi
57-
constructor(
58-
effectContext: CoroutineContext = EmptyCoroutineContext
59-
) : this(DesktopComposeUiTest(effectContext = effectContext))
60-
6179
@InternalComposeUiApi
6280
var scene: ComposeScene
6381
get() = composeTest.scene

compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/v2/ComposeTestRule.desktop.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@
1616

1717
package androidx.compose.ui.test.junit4.v2
1818

19+
import androidx.compose.ui.test.DesktopComposeUiTest
20+
import androidx.compose.ui.test.ExperimentalTestApi
21+
import androidx.compose.ui.test.InternalTestApi
1922
import androidx.compose.ui.test.junit4.ComposeContentTestRule
23+
import androidx.compose.ui.test.junit4.DesktopComposeTestRule
2024
import kotlin.coroutines.CoroutineContext
2125

22-
actual fun createComposeRule(effectContext: CoroutineContext): ComposeContentTestRule {
23-
// TODO: https://youtrack.jetbrains.com/issue/CMP-9519/Implement-ComposeUiTest-v2-APIs-and-migrate-the-tests
24-
TODO("createComposeRule v2 - Not yet implemented")
25-
}
26+
@OptIn(ExperimentalTestApi::class, InternalTestApi::class)
27+
actual fun createComposeRule(effectContext: CoroutineContext): ComposeContentTestRule =
28+
DesktopComposeTestRule(
29+
DesktopComposeUiTest(
30+
effectContext = effectContext,
31+
useStandardTestDispatcherForComposition = true
32+
)
33+
)

compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.ui.unit.Density
2020
import kotlin.coroutines.CoroutineContext
2121
import kotlin.coroutines.EmptyCoroutineContext
2222
import kotlin.time.Duration
23+
import kotlinx.coroutines.ExperimentalCoroutinesApi
2324

2425
/**
2526
* Variant of [runComposeUiTest] that allows you to specify the size of the surface.
@@ -29,11 +30,17 @@ import kotlin.time.Duration
2930
* @param effectContext The [CoroutineContext] used to run the composition. The context for
3031
* `LaunchedEffect`s and `rememberCoroutineScope` will be derived from this context.
3132
*/
33+
@Deprecated(
34+
message =
35+
"Use `androidx.compose.ui.test.v2.runDesktopComposeUiTest` instead. The v2 APIs use " +
36+
"`StandardTestDispatcher` by default to better simulate production behavior where " +
37+
"coroutines are queued rather than executed immediately.",
38+
level = DeprecationLevel.WARNING,
39+
)
3240
@ExperimentalTestApi
3341
fun runDesktopComposeUiTest(
3442
width: Int = 1024,
3543
height: Int = 768,
36-
// TODO(https://github.com/JetBrains/compose-multiplatform/issues/2960) Support effectContext
3744
effectContext: CoroutineContext = EmptyCoroutineContext,
3845
runTestContext: CoroutineContext = EmptyCoroutineContext,
3946
testTimeout: Duration = Duration.INFINITE,
@@ -46,14 +53,16 @@ fun runDesktopComposeUiTest(
4653
height = height,
4754
effectContext = effectContext,
4855
runTestContext = runTestContext,
49-
testTimeout = testTimeout
56+
testTimeout = testTimeout,
57+
useStandardTestDispatcherForComposition = false
5058
)
5159
) {
5260
runTest { block() }
5361
}
5462
}
5563
}
5664

65+
@OptIn(ExperimentalCoroutinesApi::class, InternalTestApi::class)
5766
@ExperimentalTestApi
5867
class DesktopComposeUiTest(
5968
width: Int = 1024,
@@ -62,15 +71,18 @@ class DesktopComposeUiTest(
6271
runTestContext: CoroutineContext = EmptyCoroutineContext,
6372
testTimeout: Duration = Duration.INFINITE,
6473
density: Density = Density(1f),
74+
useStandardTestDispatcherForComposition: Boolean,
6575
) : SkikoComposeUiTest(
6676
width = width,
6777
height = height,
6878
effectContext = effectContext,
6979
runTestContext = runTestContext,
7080
testTimeout = testTimeout,
7181
density = density,
82+
semanticsOwnerListener = null,
83+
windowInsets = null,
84+
useStandardTestDispatcherForComposition = useStandardTestDispatcherForComposition
7285
) {
73-
7486
private val idlingResources = mutableSetOf<IdlingResource>()
7587

7688
override fun areAllResourcesIdle(): Boolean {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2026 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.compose.ui.test.v2
18+
19+
import androidx.compose.ui.test.DesktopComposeUiTest
20+
import androidx.compose.ui.test.ExperimentalTestApi
21+
import kotlin.coroutines.CoroutineContext
22+
import kotlin.coroutines.EmptyCoroutineContext
23+
import kotlin.time.Duration
24+
import kotlinx.coroutines.test.TestCoroutineScheduler
25+
26+
/**
27+
* Variant of [runComposeUiTest] that allows you to specify the size of the surface.
28+
*
29+
* This implementation uses [kotlinx.coroutines.test.StandardTestDispatcher] by default for running
30+
* composition. This ensures that the test behavior is consistent with
31+
* [kotlinx.coroutines.test.runTest] and provides explicit control over coroutine execution order.
32+
* This means you may need to explicitly advance time or run current coroutines when testing complex
33+
* coroutine logic, as tasks are queued on the scheduler rather than running eagerly.
34+
*
35+
* @param width the desired width of the surface
36+
* @param height the desired height of the surface
37+
* @param effectContext The [CoroutineContext] used to run the composition. The context for
38+
* `LaunchedEffect`s and `rememberCoroutineScope` will be derived from this context.
39+
* @param runTestContext The [CoroutineContext] used to create the context to run the test [block].
40+
* By default [block] will run using [kotlinx.coroutines.test.StandardTestDispatcher].
41+
* [runTestContext] and [effectContext] must not share [TestCoroutineScheduler].
42+
* @param testTimeout The [Duration] within which the test is expected to complete, otherwise a
43+
* platform specific timeout exception will be thrown.
44+
* @param block The suspendable test body.
45+
*/
46+
@ExperimentalTestApi
47+
fun runDesktopComposeUiTest(
48+
width: Int = 1024,
49+
height: Int = 768,
50+
effectContext: CoroutineContext = EmptyCoroutineContext,
51+
runTestContext: CoroutineContext = EmptyCoroutineContext,
52+
testTimeout: Duration = Duration.INFINITE,
53+
block: suspend DesktopComposeUiTest.() -> Unit
54+
) {
55+
kotlinx.coroutines.test.runTest {
56+
with(
57+
DesktopComposeUiTest(
58+
width = width,
59+
height = height,
60+
effectContext = effectContext,
61+
runTestContext = runTestContext,
62+
testTimeout = testTimeout,
63+
useStandardTestDispatcherForComposition = true,
64+
)
65+
) {
66+
runTest { block() }
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)