Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test that a composable is not displayed, but also accept if it doesn't exist?

In my test of my Compose UI, I'd like to test that nothing matching my matcher is displayed. I found two ways to check that:

  1. onNode(matcher).assertDoesNotExist()
  2. onNode(matcher).assertIsNotDisplayed()

Unfortunately, in my experience so far, certain Composables after being removed respond in my tests to "does not exist" and others (particularly LazyList items) respond to "is not displayed". If it's not displayed but still exists, assertDoesNotExist() will fail. And if it doesn't exist, assertIsNotDisplayed() will fail. (!)

As one would expect, I don't care whether Compose thinks something technically "exists" or not, I just want to test if it's gone from the user's point of view. So how can I assert that either one of these would pass?

Since these are assertions, and not boolean functions, I can't use or.

If I call both of the assertions, it's guaranteed that one of them will fail.

And I haven't been able to find any matcher with semantics like "is displayed". If one did exist, I could do

onNode(matcher and isDisplayed()).assertDoesNotExist()

But there's no isDisplayed matcher that I can find.

It's easy to test the opposite, that something exists and is displayed: assertIsDisplayed() does that. How can I test the opposite of assertIsDisplayed(), that it either doesn't exist or is not displayed?

like image 208
Dan Getz Avatar asked Nov 01 '25 19:11

Dan Getz


2 Answers

There is an issue on the Google Issue Tracker that describes your problem.

The Jetpack Compose team updated the testing methods in Compose UI 1.6.0-beta01:

The children of SubcomposeLayout (and layouts like LazyColumn based on it) which are retained to be reused in future are considered deactivated. New assertIsDeactivated() test API was introduced to test such nodes. The rests of the test apis will filter out deactivated nodes by default. (I2ef84, b/187188981)

To get the updated behavior for assertDoesNotExist() and assertIsNotDisplayed(), use the following dependency:

implementation "androidx.compose.ui:ui:1.6.1" 

The reason why you needed to use assertIsNotDisplayed() previously is that LazyColumn or LazyRow recycle the item layouts. So even when an item was removed from a list, the layout of the item still existed because it was recycled for another item. As these recycle nodes will be filtered out now by the testing methods, the test methods should behave as expected.
If you want to test whether an item layout was kept for recycling, you can use the newly introduced assertIsDeactivated() assertion.

like image 192
BenjyTec Avatar answered Nov 04 '25 08:11

BenjyTec


Looking through the source code for assertIsDisplayed, I see that it gets the SemanticsNode and then checks a bunch of conditions, one of them being that the layout is "placed". I wrote my own check for that, and found that the compose items I was looking to test are indeed "not placed" when they exist but are gone from the screen:

fun SemanticsNode.isPlaced(): Boolean {
    var layoutInfo: LayoutInfo? = this.layoutInfo
    while (layoutInfo != null) {
        if (!layoutInfo.isPlaced) return false
        layoutInfo = layoutInfo.parentInfo
    }
    return true
}

fun assertGone(matcher: SemanticsMatcher, useUnmergedTree: Boolean = false) {
    for (node in composeRule.onAllNodes(matcher, useUnmergedTree).fetchSemanticsNodes()) {
        assertTrue("A node matching [${matcher.description}] isn't gone", !node.isPlaced())
    }
}
like image 20
Dan Getz Avatar answered Nov 04 '25 07:11

Dan Getz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!