Synchronizowanie testów

Testy tworzenia są domyślnie synchronizowane z interfejsem użytkownika. Gdy wywołasz twierdzenie lub działanie za pomocą ComposeTestRule, test jest synchronizowany z wyprzedzeniem i czeka, aż drzewo interfejsu użytkownika będzie nieaktywne.

Zwykle nie musisz nic robić. Należy jednak pamiętać o pewnych skrajnych przypadkach.

Po zsynchronizowaniu testu aplikacja Compose przesuwa się w czasie za pomocą wirtualnego zegara. Oznacza to, że testy w Compose nie są wykonywane w czasie rzeczywistym, więc mogą zakończyć się powodzeniem tak szybko, jak to możliwe.

Jeśli jednak nie używasz metod synchronizowania testów, nie nastąpi ponowne złożenie i 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 to wymaganie dotyczy tylko hierarchii w sekcji Tworzenie, a nie reszty aplikacji.

Wyłączanie automatycznej synchronizacji

Gdy wywołasz za pomocą ComposeTestRule (np. assertExists()) twierdzenie lub działanie, test jest synchronizowany z interfejsem tworzenia. 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 miejscu, w którym interfejs jest nadal aktywny. Aby wyłączyć automatyczną synchronizację, ustaw właściwość autoAdvance w mainClock na false:

composeTestRule.mainClock.autoAdvance = false

Zazwyczaj będziesz to robić samodzielnie. Możesz przesunąć o dokładnie jeden kadr za pomocą advanceTimeByFrame() lub o określony czas 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 założenie było wykonywane w stanie bezczynności, oczekując lub przesuwając zegar w miarę potrzeby. Niektóre operacje asynchroniczne, których wyniki wpływają na stan interfejsu użytkownika, mogą jednak być wykonywane w tle, gdy test nie jest ich świadomy.

Utwórz i zarejestruj te zasoby nieaktywne w testach, aby uwzględnić je przy podejmowaniu decyzji, czy testowana aplikacja jest zajęta czy nie. Nie musisz podejmować żadnych działań, chyba że musisz zarejestrować dodatkowe zasoby niewykorzystywane, 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 Zasobów nieczynności w Espresso, które wskazują, czy testowany obiekt jest nieczynny czy zajęty. Użyj reguły testowej w komponencie do tworzenia wiadomości, aby zarejestrować implementację IdlingResource.

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

Synchronizacja ręczna

W niektórych przypadkach musisz zsynchronizować interfejs tworzenia z innymi częściami testu lub aplikacji, którą testujesz.

Funkcja waitForIdle() czeka, aż kompozytor przejdzie w stan bezczynności, 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.

W obu przypadkach waitForIdle() czeka też na oczekujące przechodzenie przez etapy generowania i rozmieszczania elementów.

Możesz też przesunąć zegar do momentu spełnienia określonego warunku za pomocą funkcji 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 w przypadku stanu tworzenia).

Warunki oczekiwania

W przypadku każdego warunku, który zależy od pracy zewnętrznej, np. wczytywania danych lub funkcji pomiaru lub rysowania w Androidzie (czyli pomiaru lub rysowania zewnętrznego w Compose), należy użyć bardziej ogólnego pojęcia, takiego jak waitUntil():

composeTestRule.waitUntil(timeoutMs) { condition }

Możesz też użyć dowolnego z tych waitUntilasystentó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 z informacjami o testowaniu aplikacji na Androida zawiera więcej informacji o podstawach i technikach testowania.
  • Podstawy testowania: dowiedz się więcej o podstawowych koncepcjach testowania aplikacji na Androida.
  • Testy lokalne: niektóre testy możesz przeprowadzać lokalnie na swoim komputerze.
  • Testy z użyciem instrumentacji: warto też przeprowadzać testy z użyciem instrumentacji. Chodzi o testy, które są wykonywane bezpośrednio na urządzeniu.
  • Tryb ciągłej integracji: tryb ciągłej integracji umożliwia zintegrowanie testów z potokiem wdrożeniowym.
  • Testowanie różnych rozmiarów ekranu: ze względu na dużą liczbę urządzeń dostępnych dla użytkowników, należy przetestować różne rozmiary ekranu.
  • Espresso: chociaż jest ono przeznaczone do interfejsów użytkownika opartych na widokach, wiedza o Espresso może być przydatna w niektórych aspektach testowania Compose.