Cosa sono i test automatizzati?

Completato

In questa unità verranno fornite informazioni sui vantaggi dei test automatizzati e sui tipi di test che è possibile eseguire. Verranno inoltre descritte le caratteristiche dei test efficaci e alcuni strumenti disponibili.

Cosa sono i test automatizzati?

I test automatizzati prevedono l'uso di software per eseguire il codice e confrontare i risultati effettivi con quelli previsti. Si possono paragonare ai test esplorativi o manuali, per cui una persona solitamente segue le istruzioni di un piano di test per verificare che il software funzioni come previsto.

I test manuali presentano diversi vantaggi. Ma quando le dimensioni della base di codice aumentano, i test manuali di tutte le funzionalità (inclusi i casi limite) possono diventare ripetitivi, noiosi e soggetti a errori. I test automatizzati consentono di eliminare parte di questo carico e permettono ai tester manuali di concentrarsi sul loro obiettivo principale, ossia assicurarsi che l'esperienza degli utenti finali con il software sia positiva.

La piramide di test

Per quanto riguarda i test automatizzati, è prassi comune separarli in livelli. Mike Cohn propone questo concetto, la piramide di test, nel suo libro Succeeding with Agile.

Diagramma con la piramide di test che illustra il livello di unit test contrassegnato con callout 1 e i test del livello dell'interfaccia utente, contrassegnati con callout 2.

Sebbene si tratti di una versione semplicistica del modello di Cohn, il concetto illustra che la maggior parte degli sforzi si concentra sulla scrittura di test che verificano i livelli di base del software (callout 1 nella piramide), ad esempio funzioni, classi e metodi. Gli sforzi diventano progressivamente inferiori con l'unione delle funzionalità, ad esempio a livello di interfaccia utente (callout 2 nella piramide). L'idea è che, se è possibile verificare che ogni componente di livello inferiore funzioni come previsto in isolamento, poi i test nei livelli superiori dovranno solo verificare la corretta interazione tra più componenti per ottenere il risultato previsto.

Quando è opportuno scrivere i test?

La risposta dipende principalmente da specifiche esigenze e dalle competenze nella scrittura di test.

Non è mai troppo tardi per aggiungere test per il codice già scritto e distribuito. Ciò vale soprattutto per le funzionalità che si interrompono spesso o che richiedono la maggior parte del lavoro del team di test.

Per quanto riguarda i test legati alle pipeline di integrazione continua e recapito continuo, due concetti di cui si sentirà parlare sono i test continui e lo spostamento a sinistra.

I test continui sono quelli che vengono eseguiti nelle prime fasi del processo di sviluppo, quando ogni modifica si sposta attraverso la pipeline. Spostamento a sinistra significa considerare la qualità del software e i test nelle primissime fasi del processo di sviluppo.

Ad esempio, gli sviluppatori aggiungono spesso test case mentre sviluppano le loro funzionalità ed eseguono l'intera suite di test prima di inviare la modifica alla pipeline. Questo approccio consente di assicurare che il comportamento della funzionalità che stanno sviluppando sia quello previsto, senza peraltro interrompere le funzionalità esistenti.

Ecco un breve video in cui Abel Wang, Cloud Advocate presso Microsoft, spiega come assicurare il mantenimento della qualità in un piano DevOps.

Chiedi ad Abel

Per lo spostamento a sinistra è spesso necessario coinvolgere i tester nel processo di progettazione, ancora prima che venga scritto il codice per le funzionalità. Questo modello è diverso dall'"handoff", in cui le nuove funzionalità da testare vengono presentate al team di test solo dopo la progettazione e la scrittura del software. Un bug individuato in ritardo nel processo può influire sulla pianificazione di recapito del team e i bug potrebbero essere individuati settimane o anche mesi dopo che lo sviluppatore ha originariamente compilato la funzionalità.

Il compromesso

I test automatizzati implicano un compromesso. Anche se i test automatizzati permettono ai tester di concentrarsi sulla verifica dell'esperienza dell'utente finale, gli sviluppatori possono avere l'esigenza di dedicare più tempo alla scrittura e alla manutenzione del codice di test.

Tuttavia, lo scopo dei test automatizzati è assicurare che i tester ricevano solo il codice di massima qualità, ovvero codice che è stato dimostrato che funziona come previsto. Pertanto, gli sviluppatori possono risparmiare tempo dovendo gestire meno bug o evitare di riscrivere il codice a causa di un caso limite non considerato inizialmente.

Altri vantaggi

La documentazione e la possibilità di eseguire più facilmente il refactoring del codice sono altri due vantaggi offerti dai test automatizzati.

Documentazione

I piani di test manuali possono servire da documentazione per descrivere il comportamento previsto del software e il motivo per cui esistono determinate funzionalità.

Anche i test automatizzati possono essere usati allo stesso scopo. Il codice dei test automatizzati è spesso in formato leggibile dall'utente. Gli input specificati rappresentano i valori che gli utenti potrebbero immettere. Ogni output associato specifica il risultato che gli utenti dovranno aspettarsi.

In realtà, molti sviluppatori seguono il metodo di sviluppo basato su test (TDD), scrivendo il codice di test prima di implementare una nuova funzionalità. L'idea è scrivere un set di test, definiti specifiche, che inizialmente non vengono superati. Quindi lo sviluppatore scrive in modo incrementale codice per implementare la funzionalità finché non vengono superati tutti i test. Le specifiche documentano i requisiti e il metodo di sviluppo basato su test assicura che venga scritta solo la quantità necessaria di codice per implementare la funzionalità.

Refactoring

Si supponga di avere una grande base di codice da sottoporre a refactoring per velocizzare l'esecuzione di determinati componenti. Come si può sapere se il refactoring non causerà l'interruzione di parti dell'applicazione?

I test automatizzati fungono da contratto, ossia si specificano gli input e i risultati previsti. Con un set di test superati, è possibile sperimentare ed eseguire più facilmente il refactoring del codice. Quando si apporta una modifica, è sufficiente eseguire i test e verificare che continuino a essere superati. Dopo aver soddisfatto gli obiettivi di refactoring, è possibile inviare la modifica alla pipeline di compilazione in modo che tutti ne possano trarre vantaggio, ma con un rischio minore di causare interruzioni.

Quali tipi di test automatizzati sono disponibili?

Esistono molti tipi di test automatizzati, Ogni test ha uno scopo specifico. Ad esempio, è possibile eseguire test di sicurezza per verificare che solo gli utenti autorizzati possano accedere a un componente del software o a una delle relative funzionalità.

Quando si parla di integrazione continua e di pipeline di compilazione, ci si riferisce in genere ai test di sviluppo. I test di sviluppo sono quelli che è possibile eseguire prima di distribuire l'applicazione in un ambiente di test o di produzione.

Ad esempio, i lint test, un tipo di analisi statica del codice, controlla il codice sorgente per determinare se è conforme alla guida di stile del team. Il codice formattato coerentemente è più facile da leggere e gestire.

In questo modulo si eseguiranno gli unit test e i test di code coverage.

Gli unit test verificano i componenti più fondamentali del programma o della libreria, ad esempio una singola funzione o un metodo. Si specificano uno o più input insieme ai risultati previsti. Il tester esegue ogni test e verifica se i risultati effettivi corrispondono a quelli previsti.

Si supponga ad esempio di avere una funzione che esegue un'operazione aritmetica di divisione. Si potrebbero specificare alcuni valori che si prevede vengano immessi dagli utenti, oltre a valori di casi limite come 0 e -1. Se l'input specificato genera un errore o un'eccezione, è possibile verificare se la funzione genera lo stesso errore.

I test di code coverage calcolano la percentuale di codice coperta dagli unit test. Possono includere rami condizionali nel codice per assicurare che una funzione sia coperta.

Una percentuale di code coverage più alta assicura una maggiore certezza che in seguito non verrà individuato un bug nel codice che non è stato completamente testato. Non è necessario raggiungere il 100% di code coverage. Infatti, quando si inizia, è probabile che si abbia una bassa percentuale, ma che offre un punto di partenza da cui è possibile aggiungere altri test che coprono codice problematico o usato di frequente.

Mantenere l'isolamento degli unit test

Agli unit test vengono spesso associati termini come mock, stub e inserimento delle dipendenze.

Uno unit test dovrebbe verificare una singola funzione o un singolo metodo e non l'interazione tra più componenti. Tuttavia come è possibile gestire una funzione che chiama un database o un server Web?

Oltre a interrompere l'isolamento, la chiamata a un servizio esterno può rallentare le prestazioni. Se il database o il server Web diventa inattivo o altrimenti non disponibile, la chiamata può anche interrompere l'esecuzione dei test.

Applicando tecniche come il mocking e l'inserimento delle dipendenze, è possibile creare componenti che imitano questa funzionalità esterna. Si otterrà un esempio più avanti in questo modulo.

In seguito, è possibile eseguire i test di integrazione per verificare se l'applicazione funziona correttamente con un database o un server Web reale.

Come si definisce un test valido?

Sarà possibile identificare meglio un test valido man mano che si acquisisce esperienza nella scrittura di test personalizzati e nella lettura di test scritti da altri. Ecco alcune linee guida per iniziare:

  • Non eseguire test senza uno scopo: I test devono avere uno scopo e non possono essere semplicemente una voce da spuntare da un elenco di controllo. Scrivere test che verifichino se il codice fondamentale funziona come previsto e non interrompe le funzionalità esistenti.
  • Eseguire test brevi: I test dovrebbero essere completati il più rapidamente possibile, soprattutto durante le fasi di sviluppo e compilazione. Se i test vengono eseguiti ogni volta che una modifica si sposta nella pipeline potrebbero creare colli di bottiglia.
  • Assicurarsi che i test siano ripetibili: Le esecuzioni dei test dovrebbero produrre gli stessi risultati ogni volta, sia che vengano completate nel proprio computer, in quello di un collega o nella pipeline di compilazione.
  • Definire obiettivi specifici dei test: Un preconcetto comune è che i test siano destinati a coprire il codice scritto da altri. In genere, i test dovrebbero coprire solo il proprio codice. Se ad esempio si usa una raccolta di grafica open source nel progetto, non è necessario testarla.
  • Scegliere la granularità appropriata: Se ad esempio si eseguono unit test, un singolo test non dovrebbe combinare o verificare più funzioni o metodi. Testare ogni funzione separatamente e in seguito scrivere test di integrazione che verificano la corretta interazione tra più componenti.

Quali sono i tipi di strumenti di test disponibili?

Gli strumenti di test in uso dipendono dal tipo di applicazione che si sta sviluppando e dal tipo di test che si vuole eseguire. Ad esempio, è possibile usare Selenium per eseguire test dell'interfaccia utente in molti tipi di Web browser e sistemi operativi.

Indipendentemente dal linguaggio in cui è scritta l'applicazione, sono disponibili numerosi strumenti di test.

Ad esempio, per le applicazioni Java si potrebbe scegliere Checkstyle per eseguire lint test e JUnit per gli unit test.

In questo modulo si userà NUnit per gli unit test perché è diffuso nella community .NET.

Verificare le conoscenze

1.

Secondo la piramide di test, a quali tipi di test è consigliabile dedicare la maggior parte del tempo?

2.

Spostamento a sinistra fa riferimento a:

3.

Quale delle affermazioni seguenti dimostra le procedure di test ottimali?