Risorse inattive per caffè espresso

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 un CountingIdlingResource per gestire il contatore delle attività attive.
IdlingScheduledThreadPoolExecutor
Un'implementazione personalizzata di ScheduledThreadPoolExecutor. Fornisce le stesse funzionalità e capacità della classe IdlingThreadPoolExecutor, 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 di isIdleNow(). 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.
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.

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