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:
onNode(matcher).assertDoesNotExist()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?
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.
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())
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With