Concetti fondamentali sul test di app Android

Questa pagina delinea i principi fondamentali dei test delle app per Android, incluse le best practice centrali e i relativi vantaggi.

Vantaggi dei test

I test sono parte integrante del processo di sviluppo dell'app. Eseguendo test sulla tua app in modo coerente, puoi verificarne la correttezza, il comportamento funzionale e l'usabilità prima di rilasciarla pubblicamente.

Puoi testare manualmente l'app navigandoci sopra. Potresti utilizzare diversi dispositivi ed emulatori, cambiare la lingua di sistema e provare a generare ogni errore utente o attraversare ogni flusso utente.

Tuttavia, i test manuali non scalano in modo soddisfacente e può essere facile trascurare le regressioni nel comportamento dell'app. I test automatici prevedono l'utilizzo di strumenti che eseguono test per te, che sono più veloci, più ripetibili e in genere offrono feedback più strategici sulla tua app nelle prime fasi del processo di sviluppo.

Tipi di test in Android

Le applicazioni per dispositivi mobili sono complesse e devono funzionare bene in molti ambienti. Esistono molti tipi di test.

Oggetto

Ad esempio, esistono diversi tipi di test in base all'oggetto:

  • Test funzionale: la mia app fa quello che deve fare?
  • Test del rendimento: vengono eseguiti in modo rapido ed efficiente?
  • Test di accessibilità: funzionano bene con i servizi di accessibilità?
  • Test di compatibilità: funziona bene su ogni dispositivo e livello API?

Ambito

I test variano anche in base alle dimensioni o al grado di isolamento:

  • I test delle unità o i test di piccole dimensioni verificano solo una parte molto piccola dell'app, ad esempio un metodo o una classe.
  • I test end-to-end o grandi test verificano contemporaneamente parti più ampie dell'app, ad esempio uno schermo intero o il flusso utente.
  • I test di medie sono intermedi e verificano l'integrazione tra due o più unità.
I test possono essere piccoli, medi o grandi.
Figura 1: test degli ambiti in un'applicazione tipica.

Esistono molti modi per classificare i test. Tuttavia, la distinzione più importante per gli sviluppatori di app è il luogo in cui vengono eseguiti i test.

Test strumentali e locali

Puoi eseguire test su un dispositivo Android o su un altro computer:

  • Test strumentali eseguiti su un dispositivo Android, fisico o emulato. L'app viene sviluppata e installata insieme a un'app di test che inserisce comandi e legge lo stato. I test strumentali di solito sono test dell'interfaccia utente, che avviano un'app e interagiscono con essa.
  • I test locali vengono eseguiti sulla macchina di sviluppo o su un server, quindi sono chiamati anche test lato host. Sono di solito piccole e veloci, isolando il soggetto sottoposto a test dal resto dell'app.
I test possono essere eseguiti come test strumentati su un dispositivo o test locali sulla tua macchina di sviluppo.
Figura 2: diversi tipi di test a seconda di dove vengono eseguiti.

Non tutti i test delle unità sono locali e non tutti i test end-to-end vengono eseguiti su un dispositivo. Ad esempio:

  • Grande test locale: puoi utilizzare un simulatore Android eseguito in locale, ad esempio Robolectric.
  • Test strumentato di piccole dimensioni: puoi verificare che il tuo codice funzioni bene con una funzionalità framework, come un database SQLite. Puoi eseguire questo test su più dispositivi per verificare l'integrazione con più versioni di SQLite.

Esempi

Gli snippet seguenti mostrano come interagire con l'interfaccia utente in un test dell'interfaccia utente strumentata che fa clic su un elemento e verifica che venga visualizzato un altro elemento.

espresso

// When the Continue button is clicked
onView(withText("Continue"))
    .perform(click())

// Then the Welcome screen is displayed
onView(withText("Welcome"))
    .check(matches(isDisplayed()))

UI di composizione

// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()

// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()

Questo snippet mostra parte di un test delle unità per un ViewModel (test locale, lato host):

// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)

// When data is loaded
viewModel.loadData()

// Then it should be exposing data
assertTrue(viewModel.data != null)

Definizione di una strategia di test

In un mondo ideale, dovresti testare ogni riga di codice nella tua app su ogni dispositivo con cui l'app è compatibile. Sfortunatamente, questo approccio è troppo lento e costoso per essere pratico.

Una buona strategia di test trova il giusto equilibrio tra fedeltà di un test, velocità e affidabilità. La somiglianza dell'ambiente di test con un dispositivo reale determina la fedeltà del test. Test di fedeltà superiore vengono eseguiti su dispositivi emulati o sul dispositivo fisico stesso. Test di fedeltà più bassi potrebbero essere eseguiti sulla JVM della workstation locale. I test ad alta precisione sono spesso più lenti e richiedono più risorse, perciò non tutti devono essere test ad alta precisione.

Test irregolari

Gli errori si verificano anche nelle esecuzioni di test progettate e implementate correttamente. Ad esempio, quando si esegue un test su un dispositivo reale, un aggiornamento automatico potrebbe avviarsi nel mezzo di un test e causare un errore. Le condizioni di gara sottili nel tuo codice potrebbero verificarsi solo una piccola percentuale delle volte. I test che non superano il 100% delle volte sono irregolari.

Architettura testabile

Con un'architettura dell'app testabile, il codice segue una struttura che consente di testare facilmente diverse parti della stessa in isolamento. Le architetture testabili hanno altri vantaggi, come una migliore leggibilità, manutenibilità, scalabilità e riutilizzabilità.

Un'architettura non testabile produce quanto segue:

  • Test più grandi, più lenti e più irregolari. I corsi che non possono essere sottoposti a test delle unità potrebbero dover essere coperti da test di integrazione più impegnativi o test dell'interfaccia utente.
  • Minore opportunità di testare scenari diversi. I test più lunghi sono più lenti, quindi eseguire test su tutti i possibili stati di un'app potrebbe non essere realistico.

Per scoprire di più sulle linee guida relative all'architettura, consulta la guida all'architettura delle app.

Approcci al disaccoppiamento

Se riesci a estrarre parte di una funzione, una classe o un modulo dagli altri, testarlo è più semplice ed efficace. Questa pratica è nota come disaccoppiamento ed è il concetto più importante dell'architettura testabile.

Le tecniche di disaccoppiamento più comuni includono:

  • Suddividi un'app in livelli, ad esempio Presentazione, Dominio e Dati. Puoi anche suddividere un'app in moduli, uno per funzionalità.
  • Evita di aggiungere logica a entità con grandi dipendenze, come attività e frammenti. Utilizza queste classi come punti di ingresso al framework e sposta l'interfaccia utente e la logica di business altrove, ad esempio in un livello Composable, ViewModel o di dominio.
  • Evita dipendenze dirette del framework nelle classi contenenti la logica di business. Ad esempio, non utilizzare i contesti Android in ViewModels.
  • Semplifica la sostituzione delle dipendenze. Ad esempio, usa interfacce anziché implementazioni concrete. Utilizza l'inserimento delle dipendenze anche se non usi un framework DI.

Passaggi successivi

Ora che sai perché eseguire il test e quali sono i due tipi principali di test, puoi leggere la sezione Cosa testare.

In alternativa, se vuoi creare il tuo primo test e imparare con questa pratica, dai un'occhiata ai codelab di test.