Synchronizowanie testów

Testy tworzenia wiadomości są domyślnie synchronizowane z interfejsem użytkownika. Gdy wywołasz asercję lub akcję za pomocą ComposeTestRule, test zostanie zsynchronizowany z wyprzedzeniem w oczekiwaniu, aż drzewo interfejsu stanie się nieaktywne.

Normalnie nie musisz nic robić. Są jednak pewne skrajne przypadki, o których warto wiedzieć.

Po zsynchronizowaniu testu aplikacja Compose jest wyprzedzana za pomocą zegara wirtualnego. Oznacza to, że testy tworzenia wiadomości nie są prowadzone w czasie rzeczywistym, więc można je zaliczyć tak szybko, jak to możliwe.

Jeśli jednak nie użyjesz metod synchronizujących testy, nic nie zostanie zmienione, a interfejs będzie sprawiał wrażenie wstrzymanego.

@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 dotyczy to tylko hierarchii tworzenia wiadomości, a nie pozostałej części aplikacji.

Wyłączanie automatycznej synchronizacji

Gdy wywołasz asercję lub działanie za pomocą ComposeTestRule, na przykład assertExists(), test zostanie zsynchronizowany z interfejsem tworzenia wiadomości. W niektórych przypadkach możesz zatrzymać synchronizację i samodzielnie sterować zegarem. Możesz na przykład kontrolować czas wykonywania zrzutów ekranu animacji w miejscu, w którym interfejs wciąż byłby zajęty. Aby wyłączyć automatyczną synchronizację, ustaw właściwość autoAdvance w mainClock na false:

composeTestRule.mainClock.autoAdvance = false

Zwykle możesz przesunąć czas samodzielnie. Możesz przewinąć do przodu dokładnie 1 klatkę za pomocą advanceTimeByFrame() lub o konkretny czas trwania, używając advanceTimeBy():

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

Nieaktywne zasoby

Funkcja tworzenia umożliwia synchronizowanie testów i interfejsu użytkownika, tak aby każde działanie i asercja były wykonywane w stanie bezczynności, oczekując lub przesuwając zegar zgodnie z potrzebami. Niektóre operacje asynchroniczne, których wyniki wpływają na stan interfejsu użytkownika, mogą być jednak wykonywane w tle, nawet gdy test ich nie wie.

Utwórz i zarejestruj te nieaktywne zasoby w teście, aby były one uwzględniane przy podejmowaniu decyzji, czy testowana aplikacja jest zajęta czy bezczynna. Nie musisz nic robić, chyba że musisz zarejestrować dodatkowe nieaktywne zasoby, np. gdy uruchamiasz zadanie w tle, które nie jest synchronizowane z Espresso lub Compose.

Ten interfejs API jest bardzo podobny do nieaktywnych zasobów Espresso, jeśli chodzi o wskazanie, czy testowany obiekt jest nieaktywny lub zajęty. Użyj reguły testowej tworzenia wiadomości, aby zarejestrować implementację 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 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 pozwolenia rysowania i układu.

Możesz też opóźniać zegar, aż zostanie spełniony określony warunek, korzystając z advanceTimeUntil().

composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }

Zwróć uwagę, że podany warunek powinien sprawdzać stan, na który ten zegar ma wpływ (działa tylko ze stanem tworzenia).

Poczekaj na warunki

Wszystkie warunki zależne od pracy zewnętrznej, takie jak wczytywanie danych czy pomiar lub rysowanie na Androidzie (czyli pomiar lub rysowanie na zewnątrz w celu utworzenia), powinny używać bardziej ogólnej koncepcji, np. waitUntil():

composeTestRule.waitUntil(timeoutMs) { condition }

Możesz też skorzystać z pomocy 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 aplikacji na Androida zapewnia szerszy wgląd w podstawy i techniki testowania.
  • Podstawy testowania: dowiedz się więcej o podstawowych pojęciach związanych z testowaniem aplikacji na Androida.
  • Testy lokalne: niektóre testy możesz przeprowadzić lokalnie na własnej stacji roboczej.
  • Testy z instrumentacją: warto też przeprowadzać testy z użyciem instrumentów. Oznacza to, że testy przeprowadzane bezpośrednio na urządzeniu.
  • Tryb ciągłej integracji: tryb ciągłej integracji umożliwia integrację testów z potokiem wdrożenia.
  • Testowanie różnych rozmiarów ekranu: użytkownicy mają do dyspozycji wiele urządzeń, dlatego warto przetestować różne rozmiary ekranów.
  • Espresso: to narzędzie jest przeznaczone dla użytkowników korzystających z widoku danych, ale wiedza o espresso może być pomocna w niektórych aspektach testowania funkcji Compose.