Synchronizowanie testów

Testy tworzenia wiadomości są domyślnie synchronizowane z Twoim interfejsem użytkownika. Gdy wywołasz asercję lub działanie za pomocą ComposeTestRule, test zostanie zsynchronizowany wcześniej, w oczekiwaniu, aż drzewo interfejsu będzie nieaktywne.

Zazwyczaj nie musisz nic robić. Istnieją jednak skrajne przypadki, o których warto wiedzieć.

Podczas synchronizowania testu aplikacja Compose jest coraz bardziej zaawansowana w czasie przy użyciu zegara wirtualnego. Oznacza to, że testy tworzenia wiadomości nie są przeprowadzane w czasie rzeczywistym i mogą zaliczyć jak najszybciej.

Jeśli jednak nie użyjesz metod, które synchronizują testy, nie nastąpi zmiana kompozycji, a interfejs będzie wyświetlany jako 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 w usłudze Compose, a nie pozostałej części aplikacji.

Wyłączanie automatycznej synchronizacji

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

composeTestRule.mainClock.autoAdvance = false

Zwykle możesz później samodzielnie przesunąć czas. Możesz przewinąć do przodu dokładnie o 1 klatkę za pomocą funkcji advanceTimeByFrame() lub o określony czas trwania w narzędziu advanceTimeBy():

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

Nieaktywne zasoby

Usługa Compose może synchronizować testy i interfejs użytkownika, aby każde działanie i potwierdzenie było wykonywane w stanie bezczynności, oczekiwaniem lub przedłużeniem zegara odpowiednio do potrzeb. Jednak niektóre operacje asynchroniczne, których wyniki wpływają na stan UI, mogą być wykonywane w tle, gdy test ich nie wie.

Utwórz i zarejestruj te nieaktywne zasoby w teście, aby były one brane pod uwagę podczas podejmowania decyzji o tym, czy testowana aplikacja jest zajęta czy bezczynna. Nie musisz niczego robić, chyba że musisz zarejestrować dodatkowe nieaktywne zasoby, na przykład jeśli uruchomisz zadanie w tle, które nie jest zsynchronizowane z Espresso lub Compose.

Ten interfejs API jest bardzo podobny do raportu Idling Resources w Espresso, który wskazuje, czy testowany obiekt jest bezczynny czy zajęty. Użyj reguły testowej tworzenia wiadomości, aby zarejestrować implementację obiektu IdlingResource.

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

Synchronizacja ręczna

W niektórych przypadkach musisz zsynchronizować interfejs tworzenia wiadomości z innymi częściami testu lub testowaną aplikacją.

Funkcja waitForIdle() czeka, aż funkcja tworzenia wiadomości będzie nieaktywna, ale jej działanie 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.

W obu przypadkach waitForIdle() czeka też na oczekujące przebiegi renderowania i układu.

Możesz też przyspieszyć zegar, dopóki określony warunek nie zostanie spełniony za pomocą funkcji advanceTimeUntil().

composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }

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

Poczekaj na warunki

W przypadku wszystkich warunków zależnych od pracy zewnętrznej, takich jak wczytywanie danych czy pomiar lub rysowanie Androida (czyli pomiar lub rysowanie na zewnątrz w usłudze Compose), należy stosować bardziej ogólną koncepcję, taką jak waitUntil():

composeTestRule.waitUntil(timeoutMs) { condition }

Możesz też użyć dowolnego z pomocy narzędzia waitUntil:

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 testowania na Androida zawiera szersze omówienie podstaw i technik testowania.
  • Podstawy testowania: dowiedz się więcej o podstawowych założeniach testowania aplikacji na Androida.
  • Testy lokalne: niektóre testy możesz przeprowadzić lokalnie, na swojej stacji roboczej.
  • Testy instrumentowane: warto też przeprowadzać testy instrumentowane. Chodzi o testy przeprowadzane bezpośrednio na urządzeniu.
  • Tryb ciągłej integracji: tryb ciągłej integracji umożliwia integrację testów z potokiem wdrażania.
  • Przetestuj różne rozmiary ekranów: warto przetestować różne rozmiary ekranów w przypadku dużej liczby urządzeń dostępnych dla użytkowników.
  • Espresso: chociaż wiedza o Espresso jest przeznaczona dla interfejsu opartego na widoku, może okazać się przydatna w niektórych aspektach testowania funkcji Compose.