androidkotlinandroid-jetpack-composeandroid-espresso

composeRule.onNode(isDialog()).assertIsNotDisplayed() fails. OK, how do I find what Compose Dialog is displayed?


I'm writing an Android app using Jetpack Compose. It has some dialogs. I am testing with Espresso that they disappear when appropriate. When everything's working right, I can test that all dialogs have disappeared with:

composeRule.onNode(isDialog()).assertIsNotDisplayed()

And if Espresso determines there is a displayed dialog, the test fails with an AssertionError. However, the error's text is only:

Assert failed: The component is displayed!

What component, though? How do I get information on which component is displayed?

Of course, I can run the test on the emulator, set a breakpoint at the AssertionError, and look at the emulator screen. When I do that, I see no dialog. But Espresso sees a dialog. I want to see information about the dialog Espresso is seeing. How do I do that? Almost any information would do, but right now I'm finding absolutely nothing.

I see that assertIsNotDisplayed() is defined as

fun SemanticsNodeInteraction.assertIsNotDisplayed()

so that this is a SemanticsNodeInteraction, and that such an object

Represents a semantics node and the path to fetch it from the semantics tree.

I clicked around in the debugger, exploring some properties of this, but haven't yet found the path or tree.


I'm asking how to debug failed Espresso assertions, instead of posting my particular failing code, because I don't think just getting one of my tests to pass is a permanent solution. Tomorrow another test will fail, and I'll be back to square one not knowing how to debug it. If Espresso is telling me my test failed, I need to be able to find out why, if I am to debug and fix my code.


Solution

  • The way to display this is with printToLog(), as mentioned in the documentation (thanks @Edric). I found it useful to use it together with onAllNodes() instead of starting from the root:

    composeRule.onAllNodes(isDialog()).printToLog("LookAtMe", maxDepth=Int.MAX_VALUE)
    

    While maxDepth defaults to Int.MAX_VALUE in SemanticsInteraction.printToLog(), as would indeed make sense, for SemanticsInteractionCollection.printToLog(), the more useful function for this situation, maxDepth unfortunately defaults to 0 so must be set manually.


    The reason isDialog() had been returning things I had thought weren't dialogs was that in Compose 1.7 and 1.8, unlike in 1.6, ModalBottomSheet is considered a dialog, in the sense that a "dialog" is a separate root, not only those which look like Dialogs.