I test automatici ti aiutano a migliorare la qualità dell'app in diversi modi. Ad esempio, ti aiuta a eseguire la convalida, a rilevare le regressioni e a verificare la compatibilità. Una buona strategia di test ti consente di sfruttare i test automatici per concentrarti su un vantaggio importante: la produttività degli sviluppatori.
I team raggiungono livelli di produttività più elevati quando utilizzano un approccio sistematico ai test abbinato a miglioramenti dell'infrastruttura. In questo modo, puoi ricevere un feedback tempestivo sul comportamento del codice. Una buona strategia di test:
- Rileva i problemi il prima possibile.
- Si esegue rapidamente.
- Fornisce indicazioni chiare quando è necessario correggere qualcosa.
Questa pagina ti aiuterà a decidere quali tipi di test implementare, dove eseguirli e con quale frequenza.
La piramide di test
Puoi classificare i test nelle applicazioni moderne in base alle dimensioni. I piccoli test si concentrano solo su una piccola parte di codice, il che li rende rapidi e affidabili. I test di grandi dimensioni hanno un ambito ampio e richiedono configurazioni più complesse che sono difficili da gestire. Tuttavia, i test più grandi hanno una maggiore fedeltà* e possono rilevare molti più problemi contemporaneamente.
Per *fideltà si intende la somiglianza tra l'ambiente di runtime di test e l'ambiente di produzione.
La maggior parte delle app dovrebbe avere molti test piccoli e relativamente pochi test grandi. La distribuzione dei test in ogni categoria deve formare una piramide, con i test più numerosi di piccole dimensioni che formano la base e i test più grandi e meno numerosi che formano la punta.
Riduci al minimo il costo di un bug
Una buona strategia di test massimizza la produttività degli sviluppatori riducendo al minimo il costo di rilevamento dei bug.
Consideriamo un esempio di strategia potenzialmente inefficiente. Qui, il numero di test per dimensione non è organizzato in una piramide. Esistono troppi test end-to-end di grandi dimensioni e troppo pochi test dell'interfaccia utente dei componenti:
Ciò significa che vengono eseguiti troppo pochi test prima dell'unione. Se è presente un bug, i test potrebbero non rilevarlo finché non vengono eseguiti i test end-to-end giornalieri o settimanali.
È importante considerare le implicazioni di questo aspetto per il costo di identificazione e correzione dei bug e perché è importante orientare le attività di test verso test più piccoli e frequenti:
- Quando il bug viene rilevato da un test delle unità, di solito viene risolto in pochi minuti, quindi il costo è basso.
- Un test end-to-end potrebbe richiedere giorni per scoprire lo stesso bug. Ciò potrebbe coinvolgere più membri del team, riducendo la produttività complessiva e potenzialmente ritardando una release. Il costo di questo bug è più elevato.
Detto questo, una strategia di test inefficiente è meglio di nessuna strategia. Quando un bug arriva in produzione, la correzione richiede molto tempo per arrivare ai dispositivi dell'utente, a volte settimane, quindi il ciclo di feedback è il più lungo e costoso.
Una strategia di test scalabile
La piramide dei test è tradizionalmente suddivisa in tre categorie:
- Test delle unità
- Test di integrazione
- Test end-to-end.
Tuttavia, questi concetti non hanno definizioni precise, quindi i team potrebbero voler definire le categorie in modo diverso, ad esempio utilizzando 5 livelli:
- Un test di unità viene eseguito sulla macchina host e verifica una singola unità di logica funzionale senza dipendenze dal framework Android.
- Esempio: verificare gli errori uno alla volta in una funzione matematica.
- Un test di componente verifica la funzionalità o l'aspetto di un modulo o
di un componente indipendentemente dagli altri componenti del sistema. A differenza dei test di unità, la superficie di un test di componente si estende ad astrazioni superiori rispetto ai singoli metodi e classi.
- Esempio: test di screenshot per un pulsante personalizzato
- Un test delle funzionalità verifica l'interazione di due o più componenti
o moduli indipendenti. I test delle funzionalità sono più grandi e complessi e solitamente vengono eseguiti a livello di funzionalità.
- Esempio: test del comportamento dell'interfaccia utente che verificano la gestione dello stato in una schermata
- Un test di applicazione verifica la funzionalità dell'intera applicazione
sotto forma di programma binario di cui è possibile eseguire il deployment. Si tratta di test di integrazione di grandi dimensioni che utilizzano un file binario di cui è possibile eseguire il debug, ad esempio una build di sviluppo che può contenere hook di test, come sistema in test.
- Esempio: test del comportamento dell'interfaccia utente per verificare le modifiche alla configurazione in un folding, test di localizzazione e accessibilità
- Un test candidato alla release verifica la funzionalità di una build di release.
Sono simili ai test delle applicazioni, tranne per il fatto che il file binario dell'applicazione è
minimizzato e ottimizzato. Si tratta di test di integrazione end-to-end di grandi dimensioni eseguiti in un ambiente il più vicino possibile alla produzione, senza esporre l'app ad account utente pubblici o backend pubblici.
- Esempio: Critical User Journey, test di prestazioni
Questa categorizzazione tiene conto di fedeltà, tempo, ambito e livello di isolamento. Puoi avere diversi tipi di test in più livelli. Ad esempio, il livello di test dell'applicazione può contenere test di comportamento, screenshot e rendimento.
Ambito |
Accesso alla rete |
Esecuzione |
Tipo di build |
Ciclo di vita |
|
---|---|---|---|---|---|
Unità |
Metodo o classe singolo con dipendenze minime. |
No |
Locale |
Sottoponibile a debug |
Pre-unione |
Componente |
A livello di modulo o componente Più corsi insieme |
No |
Emulatore |
Sottoponibile a debug |
Prima dell'unione |
Funzionalità |
A livello di funzionalità Integrazione con componenti di proprietà di altri team |
Simulato |
|
Di cui è possibile eseguire il debug |
Pre-unione |
Applicazione |
A livello di applicazione Integrazione con funzionalità e/o servizi di proprietà di altri team |
Simulato |
Emulatore |
Di cui è possibile eseguire il debug |
Pre-unione |
Release Candidate |
A livello di applicazione Integrazione con funzionalità e/o servizi di proprietà di altri team |
Server di produzione |
Emulatore |
Build di release compressa |
Post-unione |
Decidi la categoria di prova
Come regola generale, devi considerare il livello più basso della piramide che può fornire al team il giusto livello di feedback.
Ad esempio, considera come testare l'implementazione di questa funzionalità: l'interfaccia utente di un flusso di accesso. A seconda di ciò che devi testare, sceglierai categorie diverse:
Oggetto del test |
Descrizione di ciò che viene testato |
Categoria di test |
Tipo di test di esempio |
---|---|---|---|
Logica di convalida dei moduli |
Una classe che convalida l'indirizzo email in base a un'espressione regolare e controlla che il campo della password sia stato inserito. Non ha dipendenze. |
Test delle unità |
|
Comportamento dell'interfaccia utente del modulo di accesso |
Un modulo con un pulsante che viene attivato solo dopo la convalida |
Test dei componenti |
Test del comportamento dell'interfaccia utente eseguito su Robolectric |
Aspetto dell'interfaccia utente del modulo di accesso |
Un modulo che segue una specifica UX |
Test dei componenti |
|
Integrazione con il gestore dell'autenticazione |
L'interfaccia utente che invia le credenziali a un gestore delle autorizzazioni e riceve risposte che possono contenere errori diversi. |
Test delle funzionalità |
|
Finestra di dialogo di accesso |
Schermata che mostra il modulo di accesso quando viene premuto il pulsante di accesso. |
Test delle applicazioni |
Test del comportamento dell'interfaccia utente eseguito su Robolectric |
Percorso dell'utente fondamentale: accesso |
Un flusso di accesso completo che utilizza un account di test su un server di staging |
Candidato per la release |
Test del comportamento dell'interfaccia utente di Compose end-to-end in esecuzione sul dispositivo |
In alcuni casi, l'appartenenza di un elemento a una categoria o a un'altra può essere soggettiva. Esistono altri motivi per cui un test viene spostato verso l'alto o verso il basso, ad esempio il costo dell'infrastruttura, l'instabilità e i tempi di test lunghi.
Tieni presente che la categoria di test non determina il tipo di test e che non tutte le funzionalità devono essere testate in ogni categoria.
Anche i test manuali possono essere parte della tua strategia di test. In genere, i team di QA svolgono test di release candidate, ma possono essere coinvolti anche in altre fasi. Ad esempio, test esplorativi per i bug in una funzionalità senza uno script.
Infrastruttura di test
Una strategia di test deve essere supportata da infrastrutture e strumenti per aiutare gli sviluppatori a eseguire continuamente i test e applicare regole che garantiscano il superamento di tutti i test.
Puoi classificare i test in base all'ambito per definire quando e dove eseguire i test. Ad esempio, seguendo il modello a 5 livelli:
Categoria |
Ambiente (dove) |
Attivazione (quando) |
---|---|---|
Unità |
[Locale][4] |
Ogni commit |
Componente |
Locale |
Ogni commit |
Funzionalità |
Locali ed emulatori |
Prima dell'unione, prima di unire o inviare una modifica |
Applicazione |
Local, emulatori, 1 smartphone, 1 pieghevole |
Dopo l'unione, dopo l'unione o l'invio di una modifica |
Candidato per la release |
8 smartphone diversi, 1 pieghevole, 1 tablet |
Pre-lancio |
- I test Unit e Component vengono eseguiti sul sistema di integrazione continua per ogni nuovo commit, ma solo per i moduli interessati.
- Tutti i test Unit, Component e Feature vengono eseguiti prima dell'unione o dell'invio di una modifica.
- I test dell'applicazione vengono eseguiti dopo l'unione.
- I test dei candidati al rilascio vengono eseguiti ogni notte su smartphone, pieghevoli e tablet.
- Prima di una release, i test Release Candidate vengono eseguiti su un numero elevato di dispositivi.
Queste regole possono cambiare nel tempo quando il numero di test influisce sulla produttività. Ad esempio, se sposti i test su una cadenza giornaliera, potresti ridurre i tempi di compilazione e di test del CI, ma potresti anche prolungare il ciclo di feedback.