Testare o meno, una prospettiva tecnica

Stabilisci cosa devi testare e cosa puoi escludere.

L'articolo precedente illustrava le nozioni di base degli scenari di test e il loro contenuto. Questo articolo approfondisce la creazione di scenari di test da un punto di vista tecnico, descrivendo in dettaglio cosa includere in ogni test e cosa evitare. Essenzialmente, imparerai la risposta alle vecchie domande "Che cosa testare" o "Che cosa non testare".

Che cosa testare o non testare.

Linee guida e pattern generali

Vale la pena notare che i pattern e i punti specifici sono fondamentali, indipendentemente dal fatto che tu stia conducendo test di unità, di integrazione o end-to-end. Questi principi possono e devono essere applicati a entrambi i tipi di test, pertanto rappresentano un buon punto di partenza.

Massima semplicità

Quando si tratta di scrivere test, una delle cose più importanti da ricordare è la semplicità. È importante considerare le capacità cerebrali. Il codice di produzione principale occupa spazio significativo, lasciando poco spazio per ulteriori complessità. Ciò è particolarmente vero per i test.

Se c'è meno spazio disponibile, potresti allentare la tensione nelle operazioni di test. Ecco perché è fondamentale dare la priorità alla semplicità nei test. Infatti, le best practice per il test JavaScript di Yoni Goldberg sottolineano l'importanza della Regola d'oro: il test dovrebbe sembrare un assistente e non una formula matematica complessa. In altre parole, dovresti essere in grado di capire a prima vista l'intento del test.

Non rendere i test complessi, non dovrebbero avere un approccio simile.

Dovresti puntare alla semplicità in tutti i tipi di test, indipendentemente dalla loro complessità. Infatti, più un test è complesso, più è fondamentale semplificarlo. Un modo per raggiungere questo obiettivo è attraverso una progettazione di test semplice, in cui i test sono il più semplici possibile e testare solo ciò che è necessario. Ciò significa che ogni test deve contenere un solo scenario di test, che deve essere incentrato sul test di una singola funzionalità o funzionalità specifica.

Pensaci da questo punto di vista: dovrebbe essere facile identificare cosa non ha funzionato durante la lettura di un test fallito. Per questo motivo, è importante che i test siano semplici e comprensibili. In questo modo è possibile identificare e risolvere rapidamente eventuali problemi.

Scopri cosa vale la pena

Il design del test flat incoraggia anche la concentrazione e aiuta a garantire che i test siano significativi. Ricorda che i test non devono essere creati solo per motivi di copertura, in quanto dovrebbero sempre avere uno scopo.

Non testare ogni cosa.

Non testare i dettagli di implementazione

Un problema comune nei test è che i test spesso sono progettati per testare i dettagli dell'implementazione, ad esempio l'utilizzo di selettori nei componenti o nei test end-to-end. I dettagli di implementazione si riferiscono a elementi che gli utenti del codice in genere non utilizzano, vedono o non conoscono. Questo può portare a due problemi principali nei test: i falsi negativi e i falsi positivi.

I falsi negativi si verificano quando un test ha esito negativo, anche se il codice testato è corretto. Ciò può accadere quando i dettagli di implementazione cambiano a causa di un refactoring del codice dell'applicazione. Invece, i falsi positivi si verificano quando un test ha esito positivo, anche se il codice testato non è corretto.

Una soluzione a questo problema consiste nel prendere in considerazione i diversi tipi di utenti che hai. Gli utenti finali e gli sviluppatori possono adottare un approccio diverso e possono interagire con il codice in modo diverso. Quando pianifichi i test, è essenziale considerare cosa vedranno gli utenti o cosa con cui interagiranno e fai in modo che i test dipendano da questi elementi anziché dai dettagli di implementazione.

Ad esempio, scegliere i selettori meno soggetti a modifiche può rendere i test più affidabili: data-attributes anziché i selettori CSS. Per ulteriori dettagli, leggi l'articolo Kent C. un articolo di Dodds su questo argomento oppure continua a seguirci: un articolo su questo argomento sarà disponibile più tardi.

Imbrogli: non perdere il controllo

La simulazione è un concetto ampio utilizzato nel test delle unità e talvolta nei test di integrazione. Si tratta della creazione di dati o componenti falsi per simulare le dipendenze che hanno il controllo completo dell'applicazione. Ciò consente test isolati.

L'utilizzo di simulazioni nei test può migliorare la prevedibilità, la separazione dei problemi e le prestazioni. Se devi effettuare un test che richiede l'intervento umano (ad esempio la verifica del passaporto), dovrai nasconderlo con una simulazione. Per tutti questi motivi, le simulazioni sono uno strumento prezioso da considerare.

Allo stesso tempo, gli esempi di simulazione possono influire sull'accuratezza del test, in quanto non si riferiscono alle esperienze utente reali. Quindi devi fare attenzione quando utilizzi simulazioni e stub.

È consigliabile fare una simulazione nei test end-to-end?

In generale, no. Tuttavia, a volte i deridi possono essere una salvezza, quindi non lo escluderemo completamente.

Immagina questo scenario: stai scrivendo un test per una funzionalità che coinvolge un servizio di fornitore di servizi di pagamento di terze parti. Ti trovi in un ambiente sandbox che ti ha fornito, il che significa che non sono in corso transazioni reali. Sfortunatamente, la sandbox non funziona correttamente e impedisce i test. La correzione deve essere effettuata dal fornitore di servizi di pagamento. Non devi fare altro che attendere che il fornitore risolva il problema.

In questo caso, potrebbe essere più utile ridurre la dipendenza da servizi che non puoi controllare. Ti consigliamo comunque di utilizzare la simulazione con attenzione nei test di integrazione o end-to-end, in quanto riducono il livello di confidenza dei test.

Specifiche del test: cosa fare e cosa non fare

Nel complesso, che cosa contiene un test? Ci sono differenze tra i tipi di test? Diamo un'occhiata più da vicino ad alcuni aspetti specifici personalizzati per i principali tipi di test.

Che cosa deve fare un buon test delle unità?

Un test delle unità ideale ed efficace dovrebbe:

  • Concentrati su aspetti specifici.
  • Opera in modo indipendente.
  • Include scenari su scala ridotta.
  • Utilizza nomi descrittivi.
  • Segui la sequenza AAA, se applicabile.
  • Garantisci una copertura completa dei test.
Cosa fare ✅ No ❌
Riduci il più possibile le dimensioni dei test. Prova una cosa per ogni scenario di test. Scrivi i test su unità di grandi dimensioni.
Mantieni sempre i test isolati e simulati ciò di cui hai bisogno all'esterno dell'unità. Includere altri componenti o servizi.
Mantieni i test indipendenti. Affidarsi ai test precedenti o condividere i dati dei test.
Pensa a scenari e percorsi diversi. Limitati al massimo al percorso felice o ai test negativi.
Utilizza titoli descrittivi, in modo da poter capire immediatamente di cosa tratta il test. Esegui il test solo in base al nome della funzione e, di conseguenza, non deve essere sufficientemente descrittivo: testBuildFoo() o testGetId().
Cerca di ottenere una buona copertura del codice o una gamma più ampia di scenari di test, soprattutto in questa fase. Testa da ogni classe fino al livello del database (I/O).

Quali dispositivi rientrano in un buon test di integrazione?

Un test di integrazione ideale condivide alcuni criteri anche con i test delle unità. Tuttavia, ci sono un paio di aspetti aggiuntivi che devi considerare. Un buon test di integrazione dovrebbe:

  • Simula le interazioni tra i componenti.
  • Tratta scenari reali e utilizza simulazioni o esempi.
  • Considera le prestazioni.
Cosa fare ✅ No ❌
Testa i punti di integrazione: verifica che ogni unità operi correttamente se è integrata tra loro. Testa ogni unità in maniera isolata, è questo lo scopo dei test delle unità.
Testare scenari reali: utilizzare dati di test ricavati da dati reali. Utilizza dati di test ripetitivi generati automaticamente o altri dati che non rispecchiano casi d'uso reali.
Utilizza simulazioni e stub per le dipendenze esterne in modo da mantenere il controllo del test completo. Creare dipendenze da servizi di terze parti, ad esempio richieste di rete a servizi esterni.
Utilizza una routine di pulizia prima e dopo ogni test. Dimentica l'uso di misure di pulizia all'interno dei test, altrimenti potrebbero causare errori nei test o falsi positivi, a causa della mancanza di un adeguato isolamento del test.

Quale tra i seguenti è un buon test end-to-end?

Un test end-to-end completo dovrebbe:

  • Replica le interazioni degli utenti.
  • Includere scenari fondamentali.
  • Definisci più livelli.
  • Gestire le operazioni asincrone.
  • Verifica i risultati.
  • Tieni conto del rendimento.
Cosa fare ✅ No ❌
Utilizza le scorciatoie basate su API. Scopri di più. Usa le interazioni con la UI per ogni passaggio, incluso l'hook beforeEach.
Utilizza una routine di pulizia prima di ogni test. Presta più attenzione all'isolamento dei test rispetto ai test delle unità e di integrazione, perché in questo caso il rischio di effetti collaterali è maggiore. Dimenticarti di pulire dopo ogni test. Se non esegui la pulizia dello stato, dei dati o degli effetti collaterali rimanenti, questi influiranno sugli altri test eseguiti in un secondo momento.
Considerare i test end-to-end come test di sistema. Ciò significa che devi testare l'intero stack dell'applicazione. Testa ogni unità in maniera isolata, è questo lo scopo dei test delle unità.
Usare simulazioni minime o nulle all'interno del test. Valuta con attenzione se vuoi simulare le dipendenze esterne. Fare affidamento soprattutto sulle simulazioni.
Considera le prestazioni e il carico di lavoro, ad esempio non eseguendo test eccessivi di scenari di grandi dimensioni nello stesso test. Gestisci flussi di lavoro di grandi dimensioni senza utilizzare scorciatoie.