Synchronizowanie testów

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

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

Gdy test jest zsynchronizowany, aplikacja napisana w Compose jest przesuwana w czasie za pomocą zegara wirtualnego. Oznacza to, że testy Compose nie są przeprowadzane w czasie rzeczywistym, więc mogą zakończyć się tak szybko, jak to możliwe.

Jeśli jednak nie użyjesz metod synchronizujących testy, nie nastąpi ponowne komponowanie i interfejs użytkownika 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 to wymaganie dotyczy tylko hierarchii Compose, a nie reszty aplikacji.

Wyłączanie automatycznej synchronizacji

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

composeTestRule.mainClock.autoAdvance = false

Zwykle czas jest wtedy zmieniany ręcznie. Możesz przejść dokładnie o 1 klatkę za pomocą klawisza advanceTimeByFrame() lub o określony czas za pomocą klawisza advanceTimeBy():

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

Nieaktywne zasoby

Compose może synchronizować testy i interfejs, aby każde działanie i każde sprawdzenie było wykonywane w stanie bezczynności, w razie potrzeby oczekując lub przesuwają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 wiedzieć.

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

Ten interfejs API jest bardzo podobny do zasobów bezczynnych Espresso, które wskazują, czy testowany obiekt jest bezczynny, czy zajęty. Użyj reguły testowej tworzenia, aby zarejestrować implementację IdlingResource.

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ż funkcja Compose będzie bezczynna, ale zależy od właściwości autoAdvance:

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 przejścia rysowania i układu.

Możesz też przesunąć zegar do momentu spełnienia określonego warunku za pomocą polecenia advanceTimeUntil().

composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }

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

Oczekiwanie na warunki

Każdy warunek, który zależy od pracy zewnętrznej, np. wczytywania danych lub pomiaru lub rysowania w Androidzie (czyli pomiaru lub rysowania poza kompozycją), powinien korzystać z bardziej ogólnej koncepcji, takiej jak waitUntil():

composeTestRule.waitUntil(timeoutMs) { condition }

Możesz też użyć dowolnego z tych waitUntilpomocników:

composeTestRule.waitUntilAtLeastOneExists(matcher, timeoutMs)

composeTestRule.waitUntilDoesNotExist(matcher, timeoutMs)

composeTestRule.waitUntilExactlyOneExists(matcher, timeoutMs)

composeTestRule.waitUntilNodeCount(matcher, count, timeoutMs)

Dodatkowe materiały

  • Testowanie aplikacji na Androida: główna strona docelowa dotycząca testowania na Androidzie zawiera szersze omówienie podstaw testowania i technik testowania.
  • Podstawy testowania: dowiedz się więcej o podstawowych koncepcjach związanych z testowaniem aplikacji na Androida.
  • Testy lokalne: niektóre testy możesz przeprowadzać lokalnie, na własnej stacji roboczej.
  • Testy z użyciem instrumentacji: warto też przeprowadzać testy z użyciem instrumentacji. Są to testy, które są przeprowadzane bezpośrednio na urządzeniu.
  • Ciągła integracja: Ciągła integracja umożliwia zintegrowanie testów z potokiem wdrażania.
  • Testowanie różnych rozmiarów ekranu: użytkownicy mają do dyspozycji wiele urządzeń, dlatego warto testować różne rozmiary ekranu.
  • Espresso: chociaż Espresso jest przeznaczony do interfejsów opartych na widokach, wiedza na jego temat może być przydatna w przypadku niektórych aspektów testowania Compose.