Search code examples
user-interfacetestingandroid-jetpack-compose

How to wait for next @Composable function in jetpack compose test?


Suppose I have 3 @Composable functions: Start, Loading, Result.

In the test, I call the Start function, click the Begin button on it, and the Loading function is called.

The Loading function displays the loading procedure, takes some time, and then calls the Result function.

The Result function renders a field with the text OK.

How to wait in the test for the Result or few seconds function to be drawn and check that the text is rendered OK?

composeTestRule
    .onNodeWithText("Begin")
    .performClick()

// Here you have to wait ...

composeTestRule
    .onNodeWithText("OK")
    .assertIsDisplayed() 



Solution

  • So the options are:

    1. It is possible to write to the global variable which function was last called. The disadvantage is that you will need to register in each function.
    2. Subscribe to the state of the screen through the viewmodel and track when it comes. The disadvantage is that you will need to pull the viewmodel into the test and know the code. The plus is that the test is quickly executed and does not get stuck, as is the case with a timer.
    3. I made this choice. I wrote a function for starting an asynchronous timer, that is, the application is running, the test waits, and after the timer ends, it continues checking in the test. The disadvantage is that you set a timer with a margin of time to complete the operation and the test takes a long time to idle. The advantage is that you don't have to dig into the source code.

    Implemented the function like this.

    fun asyncTimer (delay: Long = 1000) {
        AsyncTimer.start (delay)
        composeTestRule.waitUntil (
            condition = {AsyncTimer.expired},
            timeoutMillis = delay + 1000
        )
    }
    
    object AsyncTimer {
        var expired = false
        fun start(delay: Long = 1000){
            expired = false
            Timer().schedule(delay) {
                expired = true
            }
        }
    }
    

    Then I created a base class for the test and starting to write a new test, I inherit and I have the necessary ready-made functionality for tests.