Una guida rapida alla scrittura di test end-to-end con jest-puppeteer
Questo articolo fornisce una guida rapida per scrivere test end-to-end efficienti con jest-puppeteer, enfatizzando il processo di configurazione, le API comunemente usate e scenari di test pratici utilizzando una semplice app di to-do come esempio.
Come parte del nostro impegno a garantire la qualità di Logto e al miglioramento continuo, utilizziamo jest-puppeteer per i test automatizzati end-to-end. Questo ci permette di iterare rapidamente sullo sviluppo di Logto senza interruzioni.
In questo articolo, condivideremo la nostra esperienza con la scrittura di script di test jest-puppeteer efficienti utilizzando una semplice app di to-do come esempio. Il nostro obiettivo è aiutarti a iniziare rapidamente a scrivere codice di test jest-puppeteer per i tuoi progetti.
Introduzione al test end-to-end con jest-puppeteer
Il test end-to-end è un modo per assicurarsi che la tua applicazione funzioni correttamente dal punto di vista dell'utente. Per ottenere questo, utilizziamo due strumenti essenziali: Jest e Puppeteer.
Jest è un popolare framework di test JavaScript che offre un'API intuitiva per scrivere test e asserzioni. È ampiamente utilizzato per il testing di unità e integrazione.
Puppeteer è una libreria Node.js sviluppata dal team di Chrome che fornisce un'API di alto livello per controllare browser Chrome o Chromium headless. Questo lo rende una scelta ideale per automatizzare le interazioni del browser nei test end-to-end.
jest-puppeteer è un preset di Jest che abilita i test end-to-end con Puppeteer. Offre un'API semplice per avviare nuove istanze di browser e interagire con le pagine web attraverso di esse.
Ora che hai una comprensione di base degli strumenti essenziali, tuffiamoci nella configurazione dell'ambiente di test.
Configurare l'ambiente di test
Configurare l'ambiente di test per i test end-to-end con jest-puppeteer è un processo semplice che coinvolge tre passaggi principali:
- Installare le dipendenze
Nel tuo progetto (o creare un nuovo progetto), apri il terminale ed esegui:
Poi vai alla cartella node_modules/puppeteer
e installa Chromium (che è necessario per Puppeteer):
- Configurare Jest
Successivamente, devi configurare Jest per funzionare senza problemi con Puppeteer.
Crea un file di configurazione Jest (es. jest.config.js
) nella directory principale del tuo progetto se non ne hai già uno. In questo file di configurazione, specifica jest-puppeteer come preset:
Puoi personalizzare altre impostazioni Jest in questo file secondo necessità. Per maggiori informazioni sulla personalizzazione delle configurazioni di Jest, consulta la configurazione Jest.
- Scrivi i tuoi test
Crea file di test nel tuo progetto, tipicamente nominati con l'estensione .test.js
. Jest scoprirà e eseguirà automaticamente questi file di test.
Ecco un esempio tratto dalla documentazione Jest:
Quindi esegui il comando per eseguire il test:
Seguendo questi tre passaggi, avrai un ambiente di test ben configurato per condurre test end-to-end usando jest-puppeteer.
Tuttavia, tieni presente che questo è solo un esempio di base. Per informazioni più dettagliate sulla configurazione dell'ambiente, consulta la documentazione pertinente:
API comunemente usate
Nei passaggi successivi, faremo affidamento sulle API fornite da Jest, Puppeteer e jest-puppeteer per assisterci nei nostri test.
Jest offre principalmente API per organizzare i test e asserire i risultati attesi. Puoi esplorare i dettagli specifici nella documentazione.
Le API di Puppeteer sono principalmente progettate per interagire con i browser e il loro supporto per i test potrebbe non essere così intuitivo. Puoi fare riferimento alla documentazione di Puppeteer per capire le funzionalità che offre. Nei nostri esempi di test successivi, copriremo alcuni casi d'uso comuni.
Poiché le API di Puppeteer non erano originariamente progettate per il testing, utilizzarle per scrivere test può essere una sfida. Per semplificare il processo di scrittura dei test Puppeteer, jest-puppeteer include una libreria integrata chiamata expect-puppeteer
. Offre un set di API concise e facili da usare. La libreria è dettagliata nella sua documentazione, che introduce varie funzionalità utili, come mostrato negli esempi seguenti:
Nei seguenti esempi, combineremo le API fornite da queste librerie per completare i nostri scenari di test.
Ora è il momento di iniziare a imparare a scrivere codice di test utilizzando la nostra semplice app di to-do.
La semplice app di to-do
Supponendo che abbiamo una semplice app di to-do in esecuzione su http://localhost:3000
, il codice HTML principale di questa app è il seguente:
Quando eseguiamo test end-to-end, vogliamo coprire i seguenti scenari:
- Quando accediamo all'URL dell'app, l'app e i suoi dati dovrebbero essere caricati correttamente.
- Il pulsante di controllo dell'elemento dovrebbe poter essere cliccato.
- Cliccando un link esterno nelle note dell'elemento, il link dovrebbe aprirsi in una nuova scheda.
- È possibile aggiungere un elemento dal modulo.
Successivamente, scriveremo il codice di test per verificare questi scenari.
Aspettarsi che l'app e i suoi dati siano stati caricati
Consideriamo che l'app sia caricata correttamente quando le seguenti condizioni sono soddisfatte:
- Un elemento con l'ID "app" dovrebbe esistere nella pagina dopo aver acceduto all'URL dell'app.
- I dati all'interno dell'app dovrebbero essere visualizzati correttamente.
Quindi, abbiamo scritto il seguente codice di test:
Nel codice:
page.goto
è equivalente a digitare "http://localhost:3000" nel browser, permettendoti di navigare a qualsiasi URL.page.waitForSelector
viene utilizzato per attendere che un selettore CSS specifico corrisponda a un particolare elemento, con un tempo di attesa predefinito di 30 secondi.expect(page).toMatchElement
è dalla libreriaexpect-puppeteer
, è simile apage.waitForSelector
, ma supporta anche la corrispondenza del testo all'interno di un elemento. Inoltre, il timeout predefinito pertoMatchElement
è di soli 500ms.
A prima vista potrebbe sembrare perfetto, ma quando esegui questo test e lo distribuisci su un ambiente CI, occasionalmente potrebbe fallire dopo diverse esecuzioni. Il messaggio di errore indica:
Basandoci sulle informazioni che fornisce l'errore e sul fatto che questo test passa la maggior parte del tempo, possiamo dedurre che i dati richiesti dall'app potrebbero non essere sempre restituiti entro il primo 500ms dopo il caricamento dell'app. Pertanto, vogliamo aspettare che i dati siano caricati prima di fare l'asserzione.
Tipicamente, ci sono due approcci comuni per ottenere questo risultato:
- Aumentare il tempo di attesa dell'asserzione
Dal messaggio di errore, possiamo vedere che il tempo di attesa predefinito per toMatchElement
è impostato su 500ms. Possiamo aumentare il tempo di attesa aggiungendo l'opzione timeout
alla funzione come segue:
Questo approccio può ridurre l'occorrenza di test falliti in una certa misura, ma non risolve completamente il problema perché non possiamo sapere con certezza quanto tempo è necessario per il recupero dei dati.
Pertanto, utilizziamo questo approccio solo quando siamo sicuri del tempo di attesa richiesto, come in scenari tipo "un tooltip appare dopo aver passato il mouse su un elemento per più di 2 secondi.”
- Aspetta che la richiesta di rete sia completata prima di fare l'asserzione
Questo è l'approccio corretto. Anche se potremmo non sapere quanto tempo richiederà la richiesta di dati, aspettare che la richiesta di rete finisca è sempre una scelta sicura. A questo punto, possiamo usare page.waitForNavigation({ waitUntil: 'networkidle0' })
per aspettare che le richieste di rete siano completate:
In questo modo, prima di eseguire l'asserzione per l'App e i suoi dati caricati, possiamo assicurarci che le nostre richieste di rete siano già concluse. Ciò garantisce che il test produca costantemente i risultati corretti.
Aspettarsi di cliccare un pulsante specifico
Successivamente, stiamo testando la funzionalità di cliccare il pulsante di controllo all'interno di un elemento.
Nell'app di esempio, abbiamo notato che tutti gli elementi condividono la stessa struttura. I loro pulsanti di controllo hanno il selettore CSS li[class$=item] > button
, e il testo del pulsante è sempre "Check". Questo significa che non possiamo specificare direttamente quale pulsante di controllo dell'elemento cliccare. Pertanto, abbiamo bisogno di una nuova soluzione.
Supponiamo di voler cliccare il pulsante di controllo dell'elemento "Leggi il documento introduttivo di Logto". Possiamo scomporre questo processo in due passaggi:
- Primo, ottenere un riferimento a quello specifico elemento.
- Poi, cliccare il pulsante di controllo situato all'interno di quell'elemento.
Come mostrato nel codice, utilizziamo la funzione toMatchElement
per abbinare l'elemento con itemName
impostato su "Leggi il documento introduttivo di Logto". Poi, usiamo il riferimento readDocItem
per cliccare il pulsante con il testo 'Check' che si trova sotto di esso.
È importante notare che quando otteniamo il readDocItem
, il selettore CSS utilizzato è li[class$=item]:has(div[class$=itemName])
. Questo selettore corrisponde all'elemento radice li
dell'elemento, non al itemName
all'interno di esso, perché intendiamo cliccare il button
sotto il tag li
successivamente.
L'uso di expect(readDocItem).toClick
è simile a toMatchElement
. Nell'esempio di codice, passiamo { text: 'Check' }
per corrispondere ulteriormente al textContent
del pulsante. Tuttavia, puoi scegliere se abbinare o meno il contenuto del testo del pulsante in base alle tue esigenze.
Aspettarsi che un link esterno sia aperto in una nuova scheda
Successivamente, vogliamo testare se possiamo aprire un link esterno in una nuova scheda quando clicchiamo su di esso all'interno delle note dell'elemento.
Nell'app di esempio, abbiamo riscontrato che l'elemento "Leggi il documento introduttivo di Logto" ha un link esterno al documento di Logto all'interno delle sue note. Ecco il nostro codice di test:
Nel codice, utilizziamo toClick
per cliccare sul link all'interno di itemNotes
.
Successivamente, utilizziamo browser.waitForTarget
per acquisire una nuova scheda aperta con l'URL "https://docs.logto.io/docs/tutorials/get-started."
Dopo aver ottenuto la scheda, utilizziamo target.page()
per ottenere un'istanza della pagina del documento Logto e verificare ulteriormente se la pagina è caricata.
Inoltre, ricorda di chiudere la nuova pagina aperta dopo aver completato il nostro test.
Aspettarsi di creare un elemento dal modulo
Ora, vogliamo testare la funzionalità di aggiungere un elemento.
Dobbiamo riempire il nome dell'elemento e le note dell'elemento nel modulo, cliccare sul pulsante "Aggiungi" e controllare se il contenuto da noi aggiunto appare nell'elenco:
Noterai che utilizziamo la funzione expect(page).toFill(inputSelector, content)
per compilare il contenuto del modulo. Questa funzione sostituisce tutto il contenuto nell'input con il content
fornito.
Se vuoi aggiungere alcuni caratteri all'input senza sostituire il contenuto esistente, puoi usare page.type(selector, content)
per aggiungere direttamente il contenuto desiderato all'input.
Tuttavia, quando abbiamo molti campi da riempire nel nostro modulo, chiamare toFill
più volte non è conveniente. In questo caso, possiamo utilizzare il seguente approccio per compilare tutti i contenuti in una sola chiamata:
La chiave dell'oggetto fornito è l'attributo name dell'elemento input.
Riepilogo
Abbiamo trattato un semplice esempio per introdurre i requisiti di test comuni e le relative tecniche di scrittura del codice quando si utilizza jest-puppeteer per i test end-to-end. Speriamo che questo possa aiutarti a comprendere rapidamente le basi della scrittura di codice di test jest-puppeteer.