Sincronizzare i test

I test di Compose sono sincronizzati per impostazione predefinita con la tua UI. Quando chiami un'asserzione o un'azione con ComposeTestRule, il test viene sincronizzato in anticipo, attendendo fino a quando la struttura dell'interfaccia utente è inattivo.

Normalmente non è richiesta alcuna azione da parte tua. Tuttavia, ci sono alcuni casi limite che dovresti conoscere.

Quando viene sincronizzato un test, l'app Compose esegue un'avanzamento temporale utilizzando un orologio virtuale. Ciò significa che i test di Compose non vengono eseguiti in tempo reale, quindi possono superarli il più rapidamente possibile.

Tuttavia, se non utilizzi i metodi che sincronizzano i test, non verrà eseguita alcuna ricomposizione e la UI verrà messa in pausa.

@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()
}

Tieni presente che questo requisito si applica solo alle gerarchie di Compose e non al resto dell'app.

Disattivare la sincronizzazione automatica

Quando chiami un'asserzione o un'azione tramite ComposeTestRule come assertExists(), il test viene sincronizzato con l'interfaccia utente di Compose. In alcuni casi puoi decidere di interrompere la sincronizzazione e controllare l'orologio personalmente. Ad esempio, puoi controllare il tempo necessario per acquisire screenshot precisi di un'animazione nel punto in cui la UI è ancora occupata. Per disattivare la sincronizzazione automatica, imposta la proprietà autoAdvance in mainClock su false:

composeTestRule.mainClock.autoAdvance = false

In genere si avanza personalmente il tempo. Puoi avanzare esattamente di un frame con advanceTimeByFrame() o di una durata specifica con advanceTimeBy():

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

Risorse inattive

Compose può sincronizzare i test e la UI in modo che ogni azione e asserzione venga eseguita in stato di inattività, in attesa o facendo avanzare il tempo in base alle esigenze. Tuttavia, alcune operazioni asincrone i cui risultati influiscono sullo stato dell'interfaccia utente possono essere eseguite in background mentre il test non ne è a conoscenza.

Crea e registra queste risorse di inattività nel test in modo che vengano prese in considerazione per decidere se l'app sottoposta al test è occupata o inattiva. Non devi intraprendere alcuna azione, a meno che non sia necessario registrare risorse inattive aggiuntive, ad esempio se esegui un job in background che non è sincronizzato con Espresso o Compose.

Questa API è molto simile alle risorse inattive di Espresso per indicare se il soggetto sottoposto a test è inattivo o occupato. Utilizza la regola di test di Compose per registrare l'implementazione di IdlingResource.

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

Sincronizzazione manuale

In alcuni casi, devi sincronizzare la UI di Compose con altre parti del test o con l'app che stai testando.

La funzione waitForIdle() attende che Compose sia inattiva, ma dipende dalla proprietà 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.

Tieni presente che in entrambi i casi, waitForIdle() attende anche le permessi di disegno e layout in attesa.

Inoltre, puoi far avanzare l'orologio fino a quando non viene soddisfatta una determinata condizione con advanceTimeUntil().

composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }

Tieni presente che la condizione specificata dovrebbe controllare lo stato che può essere interessato da questo orologio (funziona solo con lo stato di Compose).

Attendi le condizioni

Qualsiasi condizione che dipenda dal lavoro esterno, come il caricamento dei dati o la misurazione o il disegno di Android (ovvero misura o disegna all'esterno di Compose), dovrebbe utilizzare un concetto più generale come waitUntil():

composeTestRule.waitUntil(timeoutMs) { condition }

Puoi anche utilizzare uno qualsiasi degli aiutanti di waitUntil:

composeTestRule.waitUntilAtLeastOneExists(matcher, timeoutMs)

composeTestRule.waitUntilDoesNotExist(matcher, timeoutMs)

composeTestRule.waitUntilExactlyOneExists(matcher, timeoutMs)

composeTestRule.waitUntilNodeCount(matcher, count, timeoutMs)

Risorse aggiuntive

  • Testare le app su Android: la pagina di destinazione principale per i test di Android offre una visione più ampia delle nozioni di base e delle tecniche di test.
  • Concetti fondamentali del test: scopri di più sui concetti fondamentali alla base del test di un'app per Android.
  • Test locali: puoi eseguire alcuni test localmente sulla tua workstation.
  • Test strumentati: è buona norma eseguire anche test strumentati. Vale a dire che i test vengono eseguiti direttamente sul dispositivo.
  • Integrazione continua: l'integrazione continua ti consente di integrare i test nella pipeline di deployment.
  • Prova schermi di dimensioni diverse: con alcuni dispositivi a disposizione degli utenti, ti conviene eseguire test per schermi di dimensioni diverse.
  • Espresso: sebbene sia destinata alle UI basate su View, le conoscenze di Espresso possono comunque essere utili per alcuni aspetti del test di Compose.