Una risorsa inattiva rappresenta un'operazione asincrona i cui risultati influiscono sulle operazioni successive di un test dell'interfaccia utente. Registrando le risorse inattive con Espresso, puoi convalidare queste operazioni asincrone in modo più affidabile durante il test della tua app.
Identifica quando sono necessarie risorse inattive
Espresso fornisce un sofisticato insieme di funzionalità di sincronizzazione. Questa caratteristica del framework, tuttavia, si applica solo alle operazioni che pubblicano messaggi su MessageQueue
, ad esempio una sottoclasse di View
che traccia i contenuti sullo schermo.
Poiché Espresso non è a conoscenza di altre operazioni asincrone, incluse quelle in esecuzione in un thread in background, Espresso non può fornire le sue garanzie di sincronizzazione in queste situazioni. Per informare Espresso delle operazioni a lunga esecuzione della tua app, devi registrare ciascuna di esse come risorsa inattiva.
Se non utilizzi risorse inattive durante il test dei risultati del lavoro asincrono della tua app, potresti dover utilizzare una delle seguenti soluzioni alternative errate per migliorare l'affidabilità dei test:
- Aggiunta di chiamate a
Thread.sleep()
. Se aggiungi ritardi artificiali ai test, il completamento dell'esecuzione della suite di test richiede più tempo e, a volte, i test potrebbero non riuscire se vengono eseguiti su dispositivi più lenti. Inoltre, questi ritardi non si adattano bene, poiché in una release futura l'app potrebbe dover eseguire un lavoro asincrono dispendioso in termini di tempo. - Implementazione di wrapper per i nuovi tentativi, che utilizzano un loop per verificare ripetutamente se la tua app sta ancora eseguendo il lavoro asincrono fino a quando si verifica un timeout. Anche se specifichi un numero massimo di nuovi tentativi nei test, ogni riesecuzione consuma risorse di sistema, in particolare la CPU.
- Utilizzare istanze di
CountDownLatch
, che consentono a uno o più thread di attendere il completamento di un numero specifico di operazioni eseguite in un altro thread. Questi oggetti richiedono di specificare una durata del timeout, altrimenti la tua app potrebbe essere bloccata a tempo indeterminato. I latch rendono inoltre il codice più complesso, rendendo più difficile la manutenzione.
Espresso ti consente di rimuovere queste soluzioni alternative inaffidabili dai tuoi test e di registrare invece il lavoro asincrono della tua app come risorse inattive.
Casi d'uso comuni
Quando esegui nei test operazioni simili agli esempi seguenti, valuta la possibilità di utilizzare una risorsa inattiva:
- Caricamento dei dati da internet o da un'origine dati locale.
- Stabilire connessioni con database e callback.
- Gestione dei servizi, utilizzando un servizio di sistema o un'istanza di
IntentService
. - Esecuzione di logiche di business complesse, ad esempio trasformazioni bitmap.
È particolarmente importante registrare le risorse inattive quando queste operazioni aggiornano un'interfaccia utente che viene convalidata dai test.
Esempi di implementazioni di risorse inattive
Nell'elenco seguente sono descritti diversi esempi di implementazioni di risorse inattive che puoi integrare nella tua app:
CountingIdlingResource
- Mantiene un contatore delle attività attive. Quando il contatore è zero, la risorsa associata viene considerata inattiva. Questa funzionalità è molto simile a quella di
Semaphore
. Nella maggior parte dei casi, questa implementazione è sufficiente per gestire il lavoro asincrono dell'app durante i test. UriIdlingResource
- Come per
CountingIdlingResource
, ma il contatore deve essere zero per un determinato periodo di tempo prima che la risorsa venga considerata inattiva. Questo periodo di attesa aggiuntivo tiene conto di richieste di rete consecutive, in cui un'app nel tuo thread potrebbe effettuare una nuova richiesta subito dopo aver ricevuto una risposta a una richiesta precedente. IdlingThreadPoolExecutor
- Un'implementazione personalizzata di
ThreadPoolExecutor
che tiene traccia del numero totale di attività in esecuzione nei pool di thread creati. Questa classe utilizza unCountingIdlingResource
per gestire il contatore delle attività attive. IdlingScheduledThreadPoolExecutor
- Un'implementazione personalizzata di
ScheduledThreadPoolExecutor
. Fornisce le stesse funzionalità e capacità della classeIdlingThreadPoolExecutor
, ma può anche tenere traccia delle attività programmate per il futuro o pianificate per l'esecuzione periodica.
Crea la tua risorsa inattiva
Quando utilizzi le risorse inattive nei test della tua app, potresti dover fornire funzionalità di logging o gestione delle risorse personalizzate. In questi casi, le implementazioni elencate nella sezione precedente potrebbero non essere sufficienti. Se questo è il caso, puoi estendere una di queste implementazioni di risorse inattive o crearne una personalizzata.
Se implementi la funzionalità di risorsa inattiva, tieni presente le best practice riportate di seguito, in particolare la prima:
- Richiama le transizioni allo stato di inattività al di fuori dei controlli di inattività.
- Quando l'app diventa inattiva, chiama
onTransitionToIdle()
al di fuori di qualsiasi implementazione diisIdleNow()
. In questo modo, Espresso non effettua un secondo controllo non necessario per determinare se una determinata risorsa inattiva è inattiva.
Il seguente snippet di codice illustra questo consiglio:
Kotlin
fun isIdle() { // DON'T call callback.onTransitionToIdle() here! } fun backgroundWorkDone() { // Background work finished. callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle. // Don't do any post-processing work beyond this point. Espresso now // considers your app to be idle and moves on to the next test action. }
Java
public void isIdle() { // DON'T call callback.onTransitionToIdle() here! } public void backgroundWorkDone() { // Background work finished. callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle. // Don't do any post-processing work beyond this point. Espresso now // considers your app to be idle and moves on to the next test action. }
- Registra le risorse inattive prima che ti servano.
I vantaggi della sincronizzazione associati alle risorse inattive hanno effetto solo dopo la prima chiamata del metodo
isIdleNow()
di quella risorsa da parte di Espresso.Il seguente elenco mostra diversi esempi di questa proprietà:
- Se registri una risorsa inattiva in un metodo annotato con
@Before
, la risorsa inattiva viene applicata nella prima riga di ogni test. - Se registri una risorsa inattiva all'interno di un test, la risorsa inattiva avrà effetto alla successiva azione basata su Espresso. Questo comportamento continua a verificarsi anche se l'azione successiva si trova nello stesso test dell'istruzione che registra la risorsa inattiva.
- Se registri una risorsa inattiva in un metodo annotato con
- Annulla la registrazione delle risorse inattive dopo aver finito di utilizzarle.
Per risparmiare risorse di sistema, devi annullare la registrazione delle risorse inattive non appena non ne hai più bisogno. Ad esempio, se registri una risorsa inattiva in un metodo annotato con
@Before
, è meglio annullare la registrazione della risorsa in un metodo corrispondente annotato con@After
.- Utilizza un registro di inattività per registrare e annullare la registrazione delle risorse inattive.
Se utilizzi questo container per le risorse inattive della tua app, puoi registrare e annullare la registrazione delle risorse inattive ripetutamente secondo necessità e osservare comunque un comportamento coerente.
- Mantieni solo lo stato semplice dell'app all'interno delle risorse inattive.
Ad esempio, le risorse inattive che implementi e registri non devono contenere riferimenti agli oggetti
View
.
Registra risorse inattive
Espresso fornisce una classe container in cui puoi posizionare le risorse
inattive della tua app. Questa classe, chiamata
IdlingRegistry
, è
un elemento autonomo che introduce un overhead minimo per la tua app. La classe
ti consente anche di svolgere i seguenti passaggi per migliorare la
sostenibilità della tua app:
- Crea un riferimento a
IdlingRegistry
, anziché alle risorse inattive che contiene, nei test della tua app. - Mantieni differenze nella raccolta di risorse inattive che utilizzi per ogni variante di build.
- Definisci le risorse inattive nei servizi della tua app, anziché nei componenti dell'interfaccia utente che fanno riferimento a questi servizi.
Integra le risorse inattive nella tua app
Sebbene sia possibile aggiungere risorse inattive a un'app in diversi modi, un approccio in particolare consente di mantenere l'incapsulamento dell'app e allo stesso tempo di specificare un'operazione particolare rappresentata da una determinata risorsa inattiva.
Approccio consigliato
Quando aggiungi risorse inattive nella tua app, ti consigliamo vivamente di inserire la logica delle risorse inattive nell'app stessa e di eseguire nei test solo le operazioni di registrazione e annullamento della registrazione.
Anche se crei la situazione insolita di utilizzare un'interfaccia di solo test nel codice di produzione seguendo questo approccio, puoi eseguire il wrapping delle risorse inattive intorno al codice che hai già, mantenendo le dimensioni dell'APK e il numero di metodi dell'app.
Approcci alternativi
Se preferisci non avere una logica di inattività delle risorse nel codice di produzione dell'app, esistono diverse altre strategie di integrazione attuabili:
- Crea varianti della build, ad esempio i tipi di prodotto di Gradle, e utilizza le risorse inattive solo nella build di debug della tua app.
- Utilizza un framework di inserimento delle dipendenze come Dagger per inserire nei tuoi test il grafico delle dipendenze delle risorse inattive della tua app. Se utilizzi Dagger 2, l'iniezione stessa dovrebbe provenire da un sottocomponente.
Implementa una risorsa inattiva nei test della tua app ed esponi la parte di implementazione dell'app che deve essere sincronizzata in questi test.
Attenzione: sebbene questa decisione di progettazione sembri creare un riferimento autonomo alle risorse inattive, interrompe anche l'incapsulamento in tutte le app tranne quelle più semplici.
Risorse aggiuntive
Per ulteriori informazioni sull'utilizzo di Espresso nei test Android, consulta le risorse seguenti.
Samples
- IdlingResourceSample: sincronizzazione con job in background.