简体   繁体   中英

Compose : advanceTimeBy doesn't work with animation

I've two Box es and one Button . Clicking on the Button would toggle a flag and it triggers an AnimatedVisibility animation on these Boxes.

Code

@Composable
fun TestBox() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        var flag by remember { mutableStateOf(false) }
        AnimatedVisibility(
            visible = !flag,
            enter = slideInHorizontally(animationSpec = tween(3000)) { it },
            exit = slideOutHorizontally(animationSpec = tween(3000)) { -it }
        ) {
            // Red box
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Red)
                    .testTag("red_box"),
            ) {}
        }

        AnimatedVisibility(
            visible = flag,
            enter = slideInHorizontally(animationSpec = tween(3000)) { it },
            exit = slideOutHorizontally(animationSpec = tween(3000)) { -it }
        ) {
            // Red box
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Green)
                    .testTag("green_box"),
            ) {}
        }

        Button(onClick = { flag = !flag }) {
            Text(text = "TOGGLE")
        }
    }
}

Output

在此处输入图像描述

Now I want to write a test to check if two boxes are visible in the middle of the transition. So I wrote a test like this

class BoxAnimationTest {
    
    
    @get:Rule
    val composeRule = createComposeRule()

    @Before
    fun beforeAll() {
        composeRule.setContent {
            TestBox()
        }
    }

    @Test
    fun firstTest() {
        with(composeRule) {
            mainClock.autoAdvance = false
            onNodeWithTag("red_box").assertExists() // red box visible
            onNodeWithTag("green_box").assertDoesNotExist() // green box shouldn't be visible
            onNodeWithText("TOGGLE").performClick() // clicking toggle button
            
            mainClock.advanceTimeBy(1500) // and advance time to half of total duration (3000ms)
            onNodeWithTag("green_box").assertExists() // now both green and 
            onNodeWithTag("red_box").assertExists() // [FAILED] red should be visible
            
            mainClock.advanceTimeBy(1500) // finishing the animation
            onNodeWithTag("green_box") // now green should be visible
            onNodeWithTag("red_box").assertDoesNotExist() // but red shouldn't be
        }
    }
}

But it fails at onNodeWithTag("red_box").assertExists() (2nd).

java.lang.AssertionError: Failed: assertExists.
Reason: Expected exactly '1' node but could not find any node that satisfies: (TestTag = 'red_box')

Any idea why?

Some initial investigation showed that the animations from AnimatedVisibility were never added to the Transition , because measure() was not called and slide animation is initialized during measure. As a result, we ended up having an empty Transition that finished right away. That's why the test failed. Note that adding advanceTimeByFrame() before advanceTimeBy(1500) seems to allow the test to pass. This might be useful for narrowing down the cause.

Source - KotlingLang Slack: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1644036648038349?thread_ts=1643956469.612919&cid=CJLTWPH7S

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM