Home > database >  Android Compose: how to verify that a switch is on or off inside a unit test
Android Compose: how to verify that a switch is on or off inside a unit test

Time:08-24

I have a Switch in a composable:

Switch(
    checked = false,
    modifier = Modifier.testTag("mySwitch")
)

And I'm trying to verify it from a compose unit test:

composeTestRule.onAllNodesWithTag("mySwitch")
    .assertAll(isToggleable() and isOff())

However it fails with the following exception:

java.lang.AssertionError: Failed to assertAll((ToggleableState is defined) && (ToggleableState = 'Off'))
Found '1' nodes not matching:
1) Node #8 at (l=955.0, t=387.0, r=1054.0, b=450.0)px, Tag: 'switch'
Has 4 siblings
Selector used: 'TestTag = 'mySwitch''

Apparently the Switch is neither toggleable or "on/off-eable". I've checked also with assertIsToggleable and assertIsOff separately, and both fail.

I've verified that the Switch is visible for the UI state used in the test.

Why does my test fail? It should be possible to easily test a freaking Switch. A Switch is the very definition of "Toggleable". How could I test it then, should I use custom semantic properties?


DISCLAIMER: This question is not the same as this one. I want to verify the Switch state, not to click it (that I will try later)

CodePudding user response:

My test code was correct after all. assertIs[Not]Toggleable, assertIsOff, assertIsOn, and assertAll(isToggleable()) should work on a Switch. But due to a bug (or weird undocumented feature) in Compose 1.2.1 a Switch is not marked as "Toggleable" unless you pass a callback as parameter.

I figured out this by inspecting the source code of Switch.kt. This is the part that causes the problem:

@Composable
    @OptIn(ExperimentalMaterialApi::class)
    fun Switch(
        checked: Boolean,
        onCheckedChange: ((Boolean) -> Unit)?,
        ...
    ) {
        ...
        val toggleableModifier =
            if (onCheckedChange != null) {
                Modifier.toggleable(
                    value = checked,
                    onValueChange = onCheckedChange,
                    enabled = enabled,
                    role = Role.Switch,
                    interactionSource = interactionSource,
                    indication = null
                )
            } else {
                Modifier
            }

As you can see, unless you pass the optional parameter onCheckedChange, the Switch is not made Toggleable. And that in turn will cause all the isToggleable-like assertions to throw an exception, and the same will happen with the isOn and isOff assertions since they also require the node to be "Toggleable".

So passing a dummy onCheckedChange lambda to the switch in the test code solved the problem.

  • Related