2

I've two Boxes 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

enter image description here

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?

theapache64
  • 10,926
  • 9
  • 65
  • 108

1 Answers1

3

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.

theapache64
  • 10,926
  • 9
  • 65
  • 108