Guide pratiche

Alternative alle risorse di monitoraggio di inattività nei test di Compose: le API waitUntil (aggiornate)

Lettura di 3 minuti
Jose Alcérreca
Ingegnere per le relazioni con gli sviluppatori

In questo articolo imparerai a utilizzare l'API di test waitUntil in Compose per attendere che vengano soddisfatte determinate condizioni. In alcune situazioni, questa è una buona alternativa all'utilizzo delle risorse di monitoraggio di inattività.

[Aggiornamento 2023] In breve: utilizza le nuove API waitUntil per la sincronizzazione nei test di Compose (v1.4.0+).


Che cos'è la sincronizzazione?

Un modo per classificare i test è in base al loro ambito. I test di piccole dimensioni, o test delle unità, si concentrano su piccole parti dell'app, mentre i test di grandi dimensioni, o end-to-end, coprono una parte significativa dell'app. Puoi leggere di questo e di altri tipi di test nella documentazione sui test appena aggiornata.

Premi Invio o fai clic per visualizzare l'immagine a dimensioni intere

large_0_9n_Nqkt_HHUTOQ_In_AI_b113b43bcf.png
Ambiti di test diversi in un'app

La sincronizzazione è il meccanismo che consente al test di sapere quando eseguire l'operazione successiva. Più grande è il blocco di codice che scegli di verificare, più difficile è la sincronizzazione con il test. Nei test delle unità è facile avere il controllo completo dell'esecuzione del codice da verificare. Tuttavia, man mano che aumentiamo l'ambito per includere più classi, moduli e livelli, diventa difficile per il framework di test sapere se l'app è nel mezzo di un'operazione o meno.

Premi Invio o fai clic per visualizzare l'immagine a dimensioni intere

large_correct_b1a355f41b.webp
Sincronizzazione corretta tra test e app

androidx.test e, per estensione, Compose Test, utilizzano alcuni trucchi in background per non doverti preoccupare troppo di questo aspetto. Ad esempio, se il thread principale è occupato, il test viene messo in pausa finché non può eseguire la riga successiva.

Tuttavia, non possono sapere tutto. Ad esempio, se carichi i dati in un thread in background, il framework di test potrebbe eseguire l'operazione successiva troppo presto, causando l'esito negativo del test. La situazione peggiore si verifica quando ciò accade solo in una piccola percentuale di volte, rendendo il test instabile.

Opzione 1: risorse di monitoraggio di inattività

Le risorse di monitoraggio di inattività sono una funzionalità di Espresso che consente allo sviluppatore di decidere quando l'app è occupata. Puoi utilizzarle in due modi:

1. Installandole nel framework o nella libreria che esegue un'attività che il test non può vedere.

Un buon esempio è RxIdler, che esegue il wrapping di uno scheduler RxJava. Questo è il modo preferito per registrare le risorse di monitoraggio di inattività perché ti consente di mantenere la configurazione del test separata dal codice di test.

2. Modificando il codice in fase di test per esporre esplicitamente le informazioni che indicano se l'app è occupata o meno.

Ad esempio, potresti modificare il repository (o un test double) per indicare che è occupato durante il caricamento dei dati da un'origine dati:

Questa soluzione non è ideale perché stai modificando il codice di produzione o creando test double complicati e in alcune situazioni è difficile installarli. Ad esempio, come utilizzeresti le risorse di monitoraggio di inattività in un flusso Kotlin? Qual è l'aggiornamento finale?

Invece, possiamo attendere che le cose accadano.

Opzione 2: attendere che le cose accadano… nel modo sbagliato

Il caricamento dei dati è in genere veloce, soprattutto quando si utilizzano dati fittizi, quindi perché sprecare tempo con le risorse di monitoraggio di inattività quando puoi semplicemente mettere il test in sospensione per un paio di secondi?

Questo test verrà eseguito più lentamente del necessario o avrà esito negativo. Quando hai centinaia o migliaia di test dell'interfaccia utente, vuoi che i test siano il più veloci possibile.

Inoltre, a volte gli emulatori o i dispositivi si comportano in modo anomalo e si bloccano, facendo sì che l'operazione richieda un po' più di 2000 ms, interrompendo la build. Quando hai centinaia di test, questo diventa un problema enorme.

0_DOCdjq-JpPDGV5OB.png

Opzione 3: attendere che le cose accadano nel modo giusto

Se non vuoi modificare il codice in fase di test per esporre quando è occupato, un'altra opzione è attendere che venga soddisfatta una determinata condizione, anziché attendere un periodo di tempo arbitrario.

1_jIYFxE4qlHXMi2SwW6JemA.png

In Compose, puoi utilizzare la funzione waitUntil, che accetta un'altra funzione che produce un valore booleano.

Aggiornamento del 22/03/2023: a partire da Compose 1.4.0, abbiamo aggiunto un nuovo set di API waitUntil:

[Prima della versione 1.4.0: utilizza questi helper: waitUntilExists, waitUntilNodeCount]

…e utilizzali in questo modo:

Utilizza queste API solo quando devi sincronizzare il test con l'interfaccia utente. La sincronizzazione su ogni istruzione di test modifica inutilmente il codice di test, rendendolo più difficile da gestire.

Quando dovresti utilizzarlo? Un buon caso d'uso è il caricamento dei dati da un elemento osservabile (con LiveData, Kotlin Flow o RxJava). Se l'interfaccia utente deve ricevere più aggiornamenti prima di essere considerata inattiva, potresti semplificare la sincronizzazione utilizzando waitUntil.

Ad esempio, quando raccogli un flusso da una visualizzazione:

E gli invii più elementi:

Se repository impiega un periodo di tempo indeterminato per restituire il primo risultato, il framework di test riterrà che "Caricamento" sia lo stato di inattività (il valore iniziale assegnato in collectAsState) e continuerà con l'istruzione successiva.

Pertanto, puoi rendere il test molto più affidabile se ti assicuri che l'interfaccia utente non mostri l'indicatore di caricamento:


Buon… aspetta… test!


Licenza degli snippet di codice:

  Copyright 2022 Google LLC.
SPDX-License-Identifier: Apache-2.0
Scritto da:

Continua a leggere