Synchronizowanie testów

Testy Compose są domyślnie synchronizowane z interfejsem. Gdy wywołujesz potwierdzenie lub działanie za pomocą ComposeTestRule, test jest wcześniej synchronizowany i czeka, aż drzewo interfejsu będzie nieaktywne.

Zwykle nie musisz podejmować żadnych działań. Istnieją jednak pewne przypadki brzegowe, o których warto wiedzieć.

Gdy test jest synchronizowany, czas w aplikacji Compose jest przyspieszany za pomocą wirtualnego zegara. Oznacza to, że testy Compose nie są przeprowadzane w czasie rzeczywistym, dzięki czemu mogą zakończyć się tak szybko, jak to możliwe.

Jeśli jednak nie używasz metod synchronizujących testy, nie nastąpi ponowne komponowanie, a interfejs będzie wyglądał na wstrzymany.

@Test
fun counterTest() {
    val myCounter = mutableStateOf(0) // State that can cause recompositions.
    var lastSeenValue = 0 // Used to track recompositions.
    composeTestRule.setContent {
        Text(myCounter.value.toString())
        lastSeenValue = myCounter.value
    }
    myCounter.value = 1 // The state changes, but there is no recomposition.

    // Fails because nothing triggered a recomposition.
    assertTrue(lastSeenValue == 1)

    // Passes because the assertion triggers recomposition.
    composeTestRule.onNodeWithText("1").assertExists()
}

Pamiętaj, że ten wymóg dotyczy tylko hierarchii Compose, a nie reszty aplikacji.

Wyłączanie automatycznej synchronizacji

Gdy wywołujesz potwierdzenie lub działanie za pomocą ComposeTestRule, np. assertExists(), test jest synchronizowany z interfejsem Compose. W niektórych przypadkach możesz chcieć zatrzymać tę synchronizację i samodzielnie sterować zegarem. Możesz na przykład sterować czasem, aby robić dokładne zrzuty ekranu animacji w momencie, gdy interfejs jest nadal zajęty. Aby wyłączyć automatyczną synchronizację, ustaw właściwość autoAdvance w mainClock na false:

composeTestRule.mainClock.autoAdvance = false

Zwykle będziesz wtedy samodzielnie przyspieszać czas. Możesz przyspieszyć czas o dokładnie 1 klatkę za pomocą advanceTimeByFrame() lub o określony czas trwania za pomocą advanceTimeBy():

composeTestRule.mainClock.advanceTimeByFrame()
composeTestRule.mainClock.advanceTimeBy(milliseconds)

Nieaktywne zasoby

Compose może synchronizować testy i interfejs, tak aby każde działanie i potwierdzenie było wykonywane w stanie nieaktywnym, w razie potrzeby czekając lub przyspieszając zegar. Niektóre operacje asynchroniczne, których wyniki wpływają na stan interfejsu, mogą być jednak wykonywane w tle, a test nie będzie o nich wiedział.

Utwórz i zarejestruj te nieaktywne zasoby w teście, aby były uwzględniane podczas określania, czy testowana aplikacja jest zajęta czy nieaktywna. Nie musisz podejmować żadnych działań, chyba że chcesz zarejestrować dodatkowe nieaktywne zasoby, np. jeśli uruchamiasz zadanie w tle, które nie jest zsynchronizowane z Espresso ani Compose.

Ten interfejs API jest bardzo podobny do nieaktywnych zasobów Espresso, które wskazują, czy testowany obiekt jest nieaktywny czy zajęty. Aby zarejestrować implementację IdlingResource, użyj reguły testu Compose.

composeTestRule.registerIdlingResource(idlingResource)
composeTestRule.unregisterIdlingResource(idlingResource)

Synchronizacja ręczna

W niektórych przypadkach musisz zsynchronizować interfejs Compose z innymi częściami testu lub testowanej aplikacji.

Funkcja waitForIdle() czeka, aż Compose będzie nieaktywny, ale funkcja zależy od właściwości autoAdvance property:

composeTestRule.mainClock.autoAdvance = true // Default
composeTestRule.waitForIdle() // Advances the clock until Compose is idle.

composeTestRule.mainClock.autoAdvance = false
composeTestRule.waitForIdle() // Only waits for idling resources to become idle.

Pamiętaj, że w obu przypadkach waitForIdle() czeka też na oczekujące pomiary i rysowanie pomiary.

Możesz też przyspieszyć zegar, aż zostanie spełniony określony warunek, za pomocą advanceTimeUntil().

composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }

Pamiętaj, że dany warunek powinien sprawdzać stan, na który może wpływać ten zegar (działa tylko ze stanem Compose).

Czekanie na warunki

Każdy warunek, który zależy od pracy zewnętrznej, np. wczytywania danych lub pomiarów i rysowania w Androidzie (czyli pomiarów i rysowania poza Compose), powinien używać bardziej ogólnej koncepcji, takiej jak waitUntil():

composeTestRule.waitUntil(timeoutMs) { condition }

Możesz też użyć dowolnego z waitUntil pomocników:

composeTestRule.waitUntilAtLeastOneExists(matcher, timeoutMs)

composeTestRule.waitUntilDoesNotExist(matcher, timeoutMs)

composeTestRule.waitUntilExactlyOneExists(matcher, timeoutMs)

composeTestRule.waitUntilNodeCount(matcher, count, timeoutMs)

Dodatkowe materiały