Home > Software engineering >  LaunchedEffect not called on Box while UI testing in Jetpack Compose
LaunchedEffect not called on Box while UI testing in Jetpack Compose

Time:03-29

I am running a simple Test on LaunchedEffect:

@Test
fun testSomeState() {
    composeTestRule.setContent {
        SomeComposable {
            println("click $it")
        }
    }
    composeTestRule.onNodeWithTag("Zivi")
        .performClick()
        .assertExists()
}

@Composable
fun SomeComposable(onClick: (Int) -> Unit) {
    var someState by remember { mutableStateOf(0) }

    Button(modifier = Modifier.testTag("Zivi").pointerInteropFilter {
        someState = when (it.action) {
            MotionEvent.ACTION_DOWN -> { 1 }
            MotionEvent.ACTION_UP -> { 2 }
            else -> { someState  }
        }
        true

    }, onClick = { }) {}

    LaunchedEffect(someState) {
        println("LaunchedEffect someState $someState") // prints "LaunchedEffect someState 0" 
                                                       // and then "LaunchedEffect someState 2"
        if (someState == 2) { onClick(someState) }
    }
}

This works well, however, if i change Button to a Box, like this:

@Composable
fun SomeComposable(onClick: (Int) -> Unit) {
    var someState by remember { mutableStateOf(0) }

    Box(modifier = Modifier.testTag("Zivi").pointerInteropFilter {
        someState = when (it.action) {
            MotionEvent.ACTION_DOWN -> { 1 }
            MotionEvent.ACTION_UP -> { 2 }
            else -> { someState  }
        }
        true

    }) {}

    LaunchedEffect(someState) {
        println("LaunchedEffect someState $someState") // prints "LaunchedEffect someState 0"
        if (someState == 2) { onClick(someState) }
    }
}

Then the LaunchedEffect is not called on performClick, can anyone help me understand why?

CodePudding user response:

The box composable by default doesn't have any size so technically, when the user clicks on any portion of the screen, the event doesn't get consumed by the Box.

You can verify this by adding a background(color = Color.Green) modifier to your box and observe that no section of the screen gets colored green as expected.

You can fix this by setting a size modifier for your Box.

You can change your box implementation to this and it should work. I am using fillMaxSize here

@Composable
fun SomeComposable(onClick: (Int) -> Unit) {
    var someState by remember { mutableStateOf(0) }

    Box(modifier = Modifier
        .fillMaxSize()
        .testTag("Zivi")
        .pointerInteropFilter {
            someState = when (it.action) {
                MotionEvent.ACTION_DOWN -> {
                    1
                }
                MotionEvent.ACTION_UP -> {
                    2
                }
                else -> {
                    someState
                }
            }
            true

        }) {}

    LaunchedEffect(someState) {
        println("LaunchedEffect someState $someState") // prints "LaunchedEffect someState 0"
        // and then "LaunchedEffect someState 2"
        if (someState == 2) {
            onClick(someState)
        }
    }
}
  • Related