https://kotlinlang.org logo
#compose
Title
# compose
m

mattinger

08/25/2022, 4:55 AM
So, i’m trying to test a wrapper i’m writing around ModalBottomSheetLayout, and the testing strategy i have to use seems very odd to me. I can’t seem to call the
show
or
animateTo
functions of the ModalBottomSheetState outside of a composition, or i get this error:
A MonotonicFrameClock is not available in this CoroutineContext. Callers should supply an appropriate MonotonicFrameClock using withContext
. I’ve boiled this down to a pretty simple example, which i will put in the thread. But the basic strategy i seem to have to resort to is to keep mutableState object, and then in the
setContent
block, i can create a LaunchedEffect and collect that value and call the animateTo method instead.
Copy code
@RunWith(AndroidJUnit4::class)
@Config(sdk = [Build.VERSION_CODES.P])
class ModalBottomSheetTest {
    @get:Rule
    val rule = createComposeRule()

    @OptIn(ExperimentalMaterialApi::class)
    @Test
    fun modalBottomSheetTest() {
        val sheetState = mutableStateOf(ModalBottomSheetValue.Hidden)

        val bottomSheetState = ModalBottomSheetState(
            initialValue = ModalBottomSheetValue.Hidden,
            confirmStateChange = { false }
        )

        rule.setContent {
            val sheetStateFlow = snapshotFlow { sheetState.value }

            ModalBottomSheetLayout(
                sheetState = bottomSheetState,
                sheetContent = {
                    Box(modifier = Modifier.testTag("sheetContent")) {
                        Text("hello")
                    }
                }
            ) {
                Box(modifier = Modifier.testTag("content")) { }
            }

            LaunchedEffect(key1 = true) {
                sheetStateFlow.collect {
                    if (it != bottomSheetState.currentValue) {
                        bottomSheetState.animateTo(it)
                    }
                }
            }
        }

        println(rule.onRoot().printToString())

        runTest {
            rule.onNodeWithTag("content")
                .assertExists()

            rule.onNodeWithTag("sheetContent")
                .assertIsNotDisplayed()

//            bottomSheetState.show()
            sheetState.value = ModalBottomSheetValue.Expanded

            println(rule.onRoot().printToString())

            rule.onNodeWithTag("content")
                .assertExists()

            rule.onNodeWithTag("sheetContent")
                .assertIsDisplayed()
        }
    }
}
If you uncomment the one commented line and then comment the line below it you will see the error
j

jossiwolf

08/25/2022, 5:36 AM
Animations are driven by a
MonotonicFrameClock
. You need to either create your own test clock and pass it as context (to i.e.
runBlocking
,
runTest
) or obtain a scope from composition. The scope from composition will have a frame clock that you can control through `ComposeTestRule`'s
mainClock
- this is useful when testing things like progress. See https://android-review.googlesource.com/c/platform/frameworks/support/+/1728171/
9 Views