L’autore ha selezionato Vets Who Code per ricevere una donazione come parte del programma Write for DOnations.
Introduzione
Ottenere una copertura dei test solida è essenziale per costruire fiducia nella tua applicazione web. Jest è un esecutore di test JavaScript che fornisce risorse per scrivere ed eseguire test. React Testing Library offre un insieme di assistenti per i test che strutturano i tuoi test in base alle interazioni dell’utente piuttosto che ai dettagli di implementazione dei componenti. Sia Jest che React Testing Library vengono preconfezionati con Create React App e rispettano il principio guida secondo il quale testare le app dovrebbe somigliare a come il software verrà utilizzato.
In questo tutorial, testerai codice asincrono e interazioni in un progetto di esempio contenente vari elementi dell’interfaccia utente. Utilizzerai Jest per scrivere ed eseguire test unitari, e implementerai React Testing Library come libreria di DOM (Document Object Model) di supporto per gestire l’interazione con i componenti.
Prerequisiti
Per completare questo tutorial, avrai bisogno di:
-
Node.js versione 14 o superiore installato sul tuo computer locale. Per installare Node.js su macOS o Ubuntu 18.04, segui i passaggi in Come Installare Node.js e Creare un Ambiente di Sviluppo Locale su macOS o la sezione Installazione Usando un PPA di Come Installare Node.js su Ubuntu 18.04.
-
npm
versione 5.2 o superiore sul tuo computer locale, di cui avrai bisogno per utilizzare Create React App enpx
nel progetto di esempio. Se non hai installatonpm
insieme aNode.js
, fallo adesso. Per Linux, usa il comandosudo apt install npm
.- Per far funzionare i pacchetti
npm
in questo tutorial, installa il pacchettobuild-essential
. Per Linux, usa il comandosudo apt install build-essential
.
- Per far funzionare i pacchetti
-
Git installato sul tuo computer locale. Puoi verificare se Git è installato sul tuo computer o procedere con il processo di installazione per il tuo sistema operativo con Come installare Git su Ubuntu 20.04.
-
Familiarità con React, che puoi sviluppare con la serie Come codificare in React.js. Poiché il progetto di esempio è avviato con Create React App, non è necessario installarlo separatamente.
-
Una certa familiarità con Jest come test runner o framework è utile ma non necessaria. Poiché Jest è pre-confezionato con Create React App, non è necessario installarlo separatamente.
Passaggio 1 — Configurazione del Progetto
In questo passaggio, clonerai un progetto di esempio e avvierai il set di test. Il progetto di esempio utilizza tre strumenti principali: Create React App, Jest e React Testing Library. Create React App viene utilizzato per avviare un’applicazione React monopagina. Jest viene utilizzato come esecutore di test e React Testing Library fornisce assistenti di test per strutturare i test intorno alle interazioni dell’utente.
Per iniziare, clonerai un’app React pre-costruita da GitHub. Lavorerai con l’app Directory dei Cani, che è un progetto di esempio che sfrutta la API dei Cani per costruire un sistema di ricerca e visualizzazione per una raccolta di immagini di cani basato su una razza specifica.
Per clonare il progetto da Github, apri il tuo terminale e esegui il seguente comando:
Vedrai un output simile a questo:
OutputCloning into 'doggy-directory'...
remote: Enumerating objects: 64, done.
remote: Counting objects: 100% (64/64), done.
remote: Compressing objects: 100% (48/48), done.
remote: Total 64 (delta 21), reused 55 (delta 15), pack-reused 0
Unpacking objects: 100% (64/64), 228.16 KiB | 3.51 MiB/s, done.
Cambia nella cartella doggy-directory
:
Installa le dipendenze del progetto:
Il comando npm install
installerà tutte le dipendenze del progetto definite nel file package.json
.
Dopo aver installato le dipendenze, puoi visualizzare la versione del progetto pubblicata oppure puoi eseguire l’app in locale con il seguente comando:
Se scegli di eseguire l’app in locale, si aprirà su http://localhost:3000/
. Vedrai il seguente output nel terminale:
OutputCompiled successfully!
You can now view doggy-directory in the browser.
Local: http://localhost:3000
On Your Network: http://network_address:3000
Dopo l’avvio, la pagina di accesso per l’app avrà questo aspetto:
Il progetto ha le dipendenze installate e l’applicazione è ora in esecuzione. Successivamente, apri un nuovo terminale e avvia i test con il seguente comando:
Il comando npm test
avvia i test in modalità watch interattiva con Jest come suo test runner. Quando è in modalità watch, i test vengono eseguiti automaticamente dopo che un file viene modificato. I test verranno eseguiti ogni volta che si modifica un file e ti informeranno se quella modifica ha superato i test.
Dopo aver eseguito npm test
per la prima volta, vedrai questo output nel terminale:
OutputNo tests found related to files changed since last commit.
Press `a` to run all tests, or run Jest with `--watchAll`.
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press q to quit watch mode.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press Enter to trigger a test run.
Ora che hai l’applicazione di esempio e il set di test in esecuzione, puoi iniziare i test con la pagina di destinazione.
Passaggio 2 — Testare la Pagina di Destinazione
Per impostazione predefinita, Jest cercherà file con il suffisso .test.js
e file con il suffisso .js
nelle cartelle __tests__
. Quando apporti modifiche ai file di test rilevanti, verranno rilevate automaticamente. Man mano che vengono modificate le casistiche di test, l’output verrà aggiornato automaticamente. Il file di test preparato per il progetto di esempio doggy-directory
è configurato con un codice minimo prima di aggiungere paradigmi di testing. In questo passaggio, scriverai dei test per verificare che la pagina di destinazione dell’applicazione si carichi prima di eseguire una ricerca.
Apri src/App.test.js
nel tuo editor per vedere il seguente codice:
A minimum of one test block is required in each test file. Each test block accepts two required parameters: the first argument is a string representing the name of the test case; the second argument is a function that holds the expectations of the test.
All’interno della funzione, c’è un metodo render
che React Testing Library fornisce per renderizzare il tuo componente nel DOM. Con il componente che desideri testare renderizzato nell’ambiente di test del DOM, puoi ora iniziare a scrivere del codice per asseverare la funzionalità prevista.
Aggiungerai un blocco di test al metodo render
che testerà se la pagina di destinazione viene renderizzata correttamente prima che vengano effettuate chiamate API o selezioni. Aggiungi il codice evidenziato sotto il metodo render
:
La funzione expect
viene utilizzata ogni volta che desideri verificare un certo risultato, e accetta un singolo argomento che rappresenta il valore prodotto dal tuo codice. La maggior parte delle funzioni expect
è accoppiata con una funzione matcher per asserire qualcosa su un particolare valore. Per la maggior parte di queste asserzioni, utilizzerai matchers aggiuntivi forniti da jest-dom per facilitare il controllo degli aspetti comuni trovati nel DOM. Ad esempio, .toHaveTextContent
è il matcher per la funzione expect
nella prima riga, mentre getByRole("heading")
è il selettore per ottenere l’elemento del DOM.
React Testing Library fornisce l’oggetto screen
come modo conveniente per accedere alle interrogazioni pertinenti necessarie per asserire nell’ambiente di test del DOM. Per impostazione predefinita, React Testing Library fornisce interrogazioni che consentono di individuare elementi all’interno del DOM. Ci sono tre categorie principali di interrogazioni:
getBy*
(il più comunemente usato)queryBy*
(usato quando si testa l’assenza di un elemento senza generare un errore)findBy*
(usato quando si testa il codice asincrono)
Ogni tipo di query serve uno scopo specifico che verrà definito più avanti nel tutorial. In questo passaggio, ti concentrerai sulla query getBy*
, che è il tipo di query più comune. Per vedere un elenco esaustivo delle diverse varianti di query, puoi consultare il foglio di suggerimenti per le query di React.
Di seguito è riportata un’immagine annotata della pagina di destinazione del Doggy Directory che indica ogni sezione che il primo test (sulla resa della pagina di destinazione) copre:
Ogni funzione expect
sta asserendo quanto segue (mostrato nell’immagine annotata sopra):
- Prevedi che l’elemento con il ruolo di intestazione abbia una corrispondenza di substring di Doggy Directory.
- Prevedi che l’input di selezione abbia un valore di visualizzazione esatto di Seleziona una razza.
- Prevedi che il pulsante Cerca sia disabilitato poiché non è stata effettuata una selezione.
- Prevedi che l’immagine segnaposto sia presente nel documento poiché non è stata effettuata una ricerca.
Quando hai finito, salva il file src/App.test.js
. Poiché i test vengono eseguiti in modalità di osservazione, le modifiche verranno registrate automaticamente. Se le modifiche non vengono registrate automaticamente, potrebbe essere necessario interrompere e riavviare il set di test.
Ora, quando guardi i tuoi test nel terminale, vedrai il seguente output:
Output PASS src/App.test.js
✓ renders the landing page (172 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.595 s, estimated 5 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
In questo passaggio, hai scritto un test iniziale per verificare la visualizzazione iniziale della pagina di atterraggio del Doggy Directory. Nel prossimo passaggio, imparerai come simulare una chiamata API per testare il codice asincrono.
Passaggio 3 — Simulazione del Metodo fetch
In questo passaggio, esaminerai un approccio per simulare il metodo fetch
di JavaScript. Anche se ci sono numerosi modi per ottenere questo risultato, questa implementazione utilizzerà i metodi spyOn
e mockImplementation
di Jest.
Quando ti affidi a API esterne, c’è la possibilità che la loro API si blocchi o impieghi del tempo per restituire una risposta. Simulare il metodo fetch
fornisce un ambiente coerente e prevedibile, dando maggiore fiducia nei tuoi test. Un meccanismo di simulazione API è necessario per eseguire correttamente i test che utilizzano un’API esterna.
Nota: Nello sforzo di mantenere questo progetto semplificato, si imiterà il metodo fetch. Tuttavia, si consiglia di utilizzare una soluzione più robusta come Mock Service Worker (MSW) quando si imita il codice asincrono per codebase più grandi e pronte per la produzione.
Apri src/mocks/mockFetch.js
nel tuo editor per esaminare come funziona il metodo mockFetch
:
Il metodo mockFetch
restituisce un oggetto che assomiglia strettamente alla struttura di ciò che un’invocazione di fetch
restituirebbe in risposta alle chiamate API all’interno dell’applicazione. Il metodo mockFetch
è necessario per testare la funzionalità asincrona in due aree dell’applicazione Doggy Directory: il menu a discesa che popola l’elenco delle razze e la chiamata API per recuperare le immagini dei cani quando viene eseguita una ricerca.
Chiudi src/mocks/mockFetch.js
. Ora che comprendi come verrà utilizzato il metodo mockFetch
nei tuoi test, puoi importarlo nel tuo file di test. La funzione mockFetch
verrà passata come argomento al metodo mockImplementation
e verrà quindi utilizzata come implementazione fittizia dell’API fetch.
In src/App.test.js
, aggiungi le righe evidenziate di codice per importare il metodo mockFetch
:
Questo codice imposterà e smonterà l’implementazione fittizia in modo che ogni test parta da un terreno di gioco equo.
jest.spyOn(window, "fetch");
crea una funzione mock che traccerà le chiamate al metodo fetch
collegato alla variabile globale window nel DOM.
.mockImplementation(mockFetch);
accetta una funzione che verrà utilizzata per implementare il metodo fittizio. Poiché questo comando sovrascrive l’implementazione originale di fetch
, verrà eseguito ogni volta che viene chiamato fetch
all’interno del codice dell’applicazione.
Una volta terminato, salva il file src/App.test.js
.
Ora, quando guardi i tuoi test nel terminale, riceverai il seguente output:
Output console.error
Warning: An update to App inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at App (/home/sammy/doggy-directory/src/App.js:5:31)
18 | })
19 | .then((json) => {
> 20 | setBreeds(Object.keys(json.message));
| ^
21 | });
22 | }, []);
23 |
...
PASS src/App.test.js
✓ renders the landing page (429 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.178 s, estimated 2 s
Ran all test suites related to changed files.
L’avviso ti informa che è avvenuto un aggiornamento dello stato quando non era previsto. Tuttavia, l’output indica anche che i test hanno simulato con successo il metodo fetch
.
In questo passaggio, hai simulato il metodo fetch
e lo hai incorporato in un insieme di test. Anche se il test sta passando, devi comunque occuparti dell’avviso.
Passaggio 4 — Risoluzione dell’avviso act
In questo passaggio, imparerai come risolvere l’avviso act
che è emerso dopo le modifiche del Passaggio 3.
L’avviso act
si verifica perché hai simulato il metodo fetch
, e quando il componente si monta, effettua una chiamata API per ottenere l’elenco delle razze. L’elenco delle razze è memorizzato in una variabile di stato che popola l’elemento option
all’interno dell’input di selezione.
Di seguito è riportata l’immagine di come appare l’input di selezione dopo che è stata effettuata con successo una chiamata API per popolare l’elenco delle razze:
L’avviso viene lanciato perché lo stato viene impostato dopo che il blocco di test ha completato il rendering del componente.
Per risolvere questo problema, aggiungi le modifiche evidenziate al caso di test nel file src/App.test.js
:
La parola chiave async
dice a Jest che il codice asincrono viene eseguito come risultato della chiamata API che avviene quando il componente viene montato.
A new assertion with the findBy
query verifies that the document contains an option with the value of husky
. findBy
queries are used when you need to test asynchronous code that is dependent on something being in the DOM after a period of time. Because the findBy
query returns a promise that gets resolved when the requested element is found in the DOM, the await
keyword is used within the expect
method.
Una volta completato, salva le modifiche apportate nel file src/App.test.js
.
Con le nuove aggiunte, vedrai ora che l’avviso act
non è più presente nei tuoi test:
Output PASS src/App.test.js
✓ renders the landing page (123 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.942 s, estimated 2 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
In questo passaggio, hai imparato come risolvere l’avviso act
che può verificarsi quando si lavora con il codice asincrono. Successivamente, aggiungerai un secondo caso di test per verificare le funzionalità interattive dell’applicazione Doggy Directory.
Passaggio 5 — Testare la funzionalità di ricerca
Nel passaggio finale, scriverai un nuovo caso di test per verificare la funzionalità di ricerca e visualizzazione delle immagini. Utilizzerai una varietà di query e metodi API per ottenere la copertura di test adeguata.
Torna al file src/App.test.js
nel tuo editor. In cima al file, importa la libreria compagna user-event
e il metodo asincrono waitForElementToBeRemoved
nel file di test con i comandi evidenziati:
Utilizzerai questi import successivamente in questa sezione.
Dopo il metodo test()
iniziale, aggiungi un nuovo blocco di test asincrono e renderizza il componente App
con il seguente blocco di codice:
Con il componente renderizzato, ora puoi aggiungere funzioni che verificano le funzionalità interattive dell’app Doggy Directory.
Ancora nel file src/App.test.js
, aggiungi i blocchi di codice evidenziati all’interno del secondo metodo test()
:
La sezione evidenziata sopra simulerà la selezione di una razza di cane e verificherà che venga visualizzato il valore corretto.
La query getByRole
acquisisce l’elemento selezionato e lo assegna alla variabile select
.
Similmente a come hai risolto l’avviso act
nel Passo 4, utilizza la query findByRole
per attendere che l’opzione cattledog
appaia nel documento prima di procedere con ulteriori asserzioni.
L’oggetto userEvent
importato in precedenza simulerà le interazioni comuni dell’utente. In questo esempio, il metodo selectOptions
seleziona l’opzione cattledog
che hai atteso sulla riga precedente.
L’ultima riga verifica che la variabile select
contenga il valore cattledog
selezionato in precedenza.
La prossima sezione che aggiungerai al blocco di codice Javascript test()
inizierà la richiesta di ricerca per trovare immagini di cani in base alla razza selezionata e confermerà la presenza di uno stato di caricamento.
Aggiungi le linee evidenziate:
La query getByRole
individua il pulsante di ricerca e lo assegna alla variabile searchBtn
.
Il matcher toBeDisabled
di jest-dom verificherà che il pulsante di ricerca non sia disabilitato quando viene effettuata una selezione della razza.
Il metodo click
sull’oggetto userEvent
simula il clic sul pulsante di ricerca.
La funzione waitForElementToBeRemoved
helper asincrono importato in precedenza attenderà l’apparizione e la scomparsa del messaggio Loading mentre la chiamata API di ricerca è in corso. queryByText
all’interno della funzione di richiamata waitForElementToBeRemoved
controlla l’assenza di un elemento senza generare un errore.
L’immagine qui sotto mostra lo stato di caricamento che verrà visualizzato durante una ricerca in corso:
Successivamente, aggiungi il seguente codice Javascript per convalidare l’immagine e il conteggio dei risultati visualizzati:
La query getAllByRole
selezionerà tutte le immagini di cane e le assegnerà alla variabile dogImages
. La variante *AllBy*
della query restituisce un array contenente più elementi che corrispondono al ruolo specificato. La variante *AllBy*
differisce dalla variante ByRole
, che può restituire solo un singolo elemento.
L’implementazione falsificata di fetch
conteneva due URL di immagine all’interno della risposta. Con l’asserzione toHaveLength
di Jest, puoi verificare che vengano visualizzate due immagini.
La query getByText
controllerà che il conteggio dei risultati corretti appaia nell’angolo destro.
Due asserzioni utilizzando i matcher toHaveAccessibleName
verificano che il testo alternativo appropriato sia associato alle singole immagini.
A completed search displaying images of the dog based on the breed selected along with the number of results found will look like this:
Quando si combinano tutte le parti del nuovo codice Javascript, il file App.test.js
avrà questo aspetto:
Salva le modifiche apportate in src/App.test.js
.
Quando si revisionano i test, l’output finale nel terminale avrà ora il seguente output:
Output PASS src/App.test.js
✓ renders the landing page (273 ms)
✓ should be able to search and display dog image results (123 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.916 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
In questo ultimo passaggio, è stato aggiunto un test che verifica le funzionalità di ricerca, caricamento e visualizzazione dell’applicazione Doggy Directory. Con l’ultima asserzione scritta, ora si sa che l’app funziona.
Conclusione
Durante questo tutorial, hai scritto casi di test utilizzando Jest, React Testing Library e i matchers jest-dom. Costruendo incrementalmente, hai scritto test basati su come un utente interagisce con l’interfaccia utente. Hai anche appreso le differenze tra le query getBy*
, findBy*
e queryBy*
e come testare il codice asincrono.
Per saperne di più sugli argomenti sopra menzionati, dai un’occhiata alla documentazione ufficiale di Jest, React Testing Library e jest-dom. Puoi anche leggere il libro di Kent C. Dodd sui Errori Comuni con React Testing Library per imparare le migliori pratiche quando si lavora con React Testing Library. Per saperne di più sull’utilizzo dei test snapshot all’interno di un’app React, dai un’occhiata a Come Scrivere Test Snapshot.