Un viaggio di test E2E Parte 2: WebdriverIO a Cypress
Pubblicato: 2019-11-21Nota: questo è un post di #frontend@twiliosendgrid. Per altri post di ingegneria, vai al rotolo del blog tecnico.
In tutte le nostre app frontend, avevamo e abbiamo tuttora il seguente obiettivo: fornire un modo per scrivere test di automazione E2E (end-to-end) coerenti, di cui è possibile eseguire il debug, manutenibili e di valore per le nostre applicazioni front-end e integrarli con CICD (integrazione continua e distribuzione continua).
Per raggiungere lo stato attuale in cui centinaia di test E2E possono essere attivati o eseguiti in base a un programma in tutti i nostri team di front-end in SendGrid, abbiamo dovuto ricercare e sperimentare molte potenziali soluzioni lungo il percorso fino a quando non abbiamo raggiunto quello principale obbiettivo.
Abbiamo tentato di implementare la nostra soluzione Ruby Selenium personalizzata sviluppata da ingegneri di test dedicati chiamati SiteTestUI alias STUI, in cui tutti i team potevano contribuire a un repository ed eseguire test di automazione cross-browser. Purtroppo ha ceduto a test lenti e traballanti, falsi allarmi, mancanza di contesto tra repository e linguaggi, troppe mani in un paniere, esperienze di debug dolorose e più tempo dedicato alla manutenzione che alla fornitura di valore.
Abbiamo quindi sperimentato un'altra promettente libreria in WebdriverIO per scrivere test in JavaScript in co-locazione con i repository di applicazioni di ciascun team. Sebbene ciò risolvesse alcuni problemi, rafforzasse la proprietà del team e ci consentisse di integrare i test con Buildkite, il nostro provider CICD, avevamo ancora test traballanti che erano dolorosi da eseguire il debug e difficili da scrivere oltre a gestire bug e stranezze del selenio fin troppo simili .
Volevamo evitare un altro STUI 2.0 e abbiamo iniziato a esplorare altre opzioni. Se desideri saperne di più sulle nostre lezioni apprese e sulle strategie scoperte lungo il percorso durante la migrazione da STUI a WebdriverIO, dai un'occhiata alla parte 1 della serie di post sul blog.
In questa seconda parte finale della serie di post sul blog, tratteremo il nostro viaggio da STUI e WebdriverIO a Cypress e come abbiamo attraversato migrazioni simili nella configurazione dell'infrastruttura complessiva, nella scrittura di test E2E organizzati, nell'integrazione con le nostre pipeline Buildkite e nel ridimensionamento a altri team di front-end nell'organizzazione.
TLDR: Abbiamo adottato Cypress su STUI e WebdriverIO e abbiamo raggiunto tutti i nostri obiettivi di scrivere preziosi test E2E da integrare con CICD. Gran parte del nostro lavoro e delle lezioni apprese da WebdriverIO e STUI sono stati perfettamente riportati nel modo in cui utilizziamo e integriamo i test Cypress oggi.
Sommario
Esplorazione e atterraggio su Cypress
Passaggio da STUI e WebdriverIO a Cypress
Passaggio 1: installazione delle dipendenze per Cypress
Passaggio 2: configurazioni e script dell'ambiente
Primo passaggio di configurazioni e script dell'ambiente
Evoluzione delle configurazioni e degli script dell'ambiente
Passaggio 3: implementazione dei test E2E in locale
Passaggio 4: Dockerizzazione dei test
Passaggio 5: integrazione con CICD
Passaggio 6: confronto tra Cypress e WebdriverIO/STUI
Passaggio 7: ridimensionamento ad altri team frontend
Quello che non vediamo l'ora con Cypress
Adozione del cipresso nel futuro
Esplorazione e atterraggio su Cypress
Quando abbiamo cercato alternative a WebdriverIO, abbiamo visto altri wrapper Selenium come Protractor e Nightwatch con un set di funzionalità simile a WebdriverIO, ma sentivamo che molto probabilmente ci saremmo imbattuti in lunghe configurazioni, test traballanti e noiosi debug e manutenzione lungo la strada.
Per fortuna ci siamo imbattuti in un nuovo framework di test E2E chiamato Cypress, che mostrava configurazioni rapide, test veloci e di cui è possibile eseguire il debug eseguiti nel browser, stubbing delle richieste a livello di rete e, soprattutto, non utilizzare Selenium sotto il cofano.
Ci siamo meravigliati delle fantastiche funzionalità come le registrazioni video, la GUI di Cypress, il servizio dashboard a pagamento e la parallelizzazione da provare. Eravamo disposti a scendere a compromessi sul supporto cross-browser a favore di test preziosi che superavano costantemente Chrome con un repertorio di strumenti a nostra disposizione per implementare, eseguire il debug e mantenere i test per i nostri sviluppatori e QA.
Abbiamo anche apprezzato i framework di test, le librerie di asserzioni e tutti gli altri strumenti scelti per noi per avere un approccio più standardizzato ai test in tutti i nostri team frontend. Di seguito, abbiamo fornito uno screenshot delle differenze tra una soluzione come WebdriverIO e Cypress e se desideri vedere più differenze tra le soluzioni Cypress e Selenium, puoi consultare la loro documentazione su come funziona.
Con questo in mente, ci siamo impegnati a testare Cypress come un'altra soluzione per scrivere test E2E veloci, coerenti e di cui è possibile eseguire il debug da integrare eventualmente con Buildkite durante CICD. Abbiamo anche deliberatamente fissato un altro obiettivo di confrontare Cypress con le precedenti soluzioni basate sul selenio per determinare in definitiva in base ai punti dati se dovremmo continuare a cercare o costruire le nostre suite di test con Cypress in futuro. Abbiamo pianificato di convertire i test WebdriverIO esistenti e qualsiasi altro test ad alta priorità ancora in STUI in test Cypress e confrontare le nostre esperienze di sviluppo, velocità, stabilità, tempi di esecuzione dei test e manutenzione dei test.
Passaggio da STUI e WebdriverIO a Cypress
Quando siamo passati da STUI e WebdriverIO a Cypress, lo abbiamo affrontato sistematicamente attraverso la stessa strategia di alto livello che abbiamo utilizzato quando abbiamo tentato la nostra migrazione da STUI a WebdriverIO nei nostri repository di applicazioni frontend. Per maggiori dettagli su come abbiamo eseguito tali passaggi per WebdriverIO, fare riferimento alla parte 1 della serie di post sul blog. I passaggi generali per il passaggio a Cypress hanno comportato quanto segue:
- Installazione e configurazione delle dipendenze per il collegamento con Cypress
- Stabilire le configurazioni dell'ambiente e i comandi degli script
- Implementazione di test E2E che superano localmente ambienti diversi
- Dockerizzazione dei test
- Integrazione dei test Dockerizzati con Buildkite, il nostro provider CICD
Per raggiungere i nostri obiettivi secondari, abbiamo anche aggiunto ulteriori passaggi per confrontare Cypress con le precedenti soluzioni Selenium e per adattare Cypress a tutti i team front-end dell'organizzazione:
6. Confronto di Cypress in termini di esperienze di sviluppo, velocità e stabilità dei test rispetto a WebdriverIO e STUI
7. Ridimensionamento ad altri team frontend
Passaggio 1: installazione delle dipendenze per Cypress
Per iniziare e funzionare rapidamente con Cypress, tutto ciò che dovevamo fare era `npm install cypress` nei nostri progetti e avviare Cypress per la prima volta affinché fosse automaticamente disposto con un file di configurazione `cypress.json` e una cartella cypress
con dispositivi di avviamento, test e altri file di configurazione per comandi e plug-in. Abbiamo apprezzato il modo in cui Cypress è stato fornito in bundle con Mocha come test runner, Chai per le asserzioni e Chai-jQuery e Sinon-Chai per ancora più asserzioni da usare e concatenare. Non abbiamo più dovuto dedicare molto tempo alla ricerca su quali test runner, reporter, asserzioni e librerie di servizi installare e utilizzare rispetto a quando abbiamo iniziato con WebdriverIO o STUI. Abbiamo immediatamente eseguito alcuni dei test generati con la loro GUI Cypress ed esplorato le numerose funzionalità di debug a nostra disposizione come il debug dei viaggi nel tempo, il parco giochi del selettore, i video registrati, gli screenshot, i registri dei comandi, gli strumenti per sviluppatori del browser, ecc.
Lo abbiamo anche configurato in seguito con Eslint e TypeScript per il controllo del tipo statico aggiuntivo e le regole di formattazione da seguire quando si esegue il commit del nuovo codice di test Cypress. Inizialmente abbiamo avuto alcuni problemi con il supporto TypeScript e alcuni file dovevano essere file JavaScript come quelli incentrati sui file dei plugin, ma per la maggior parte siamo stati in grado di controllare la digitazione della maggior parte dei nostri file per i nostri test, oggetti di pagina e comandi.
Ecco una struttura di cartelle di esempio che uno dei nostri team di frontend ha seguito per incorporare oggetti di pagina, plug-in e comandi:
Passaggio 2: configurazioni e script dell'ambiente
Dopo aver installato e configurato rapidamente Cypress per l'esecuzione in locale, avevamo bisogno di un modo per eseguire i nostri test Cypress con impostazioni diverse per ogni ambiente e volevamo supportare gli stessi casi d'uso che i nostri comandi WebdriverIO ci consentivano di fare. Ecco un elenco che illustra la maggior parte dei casi d'uso di come volevamo eseguire questi test e perché desideravamo supportarli:
- Contro un server di sviluppo Webpack in esecuzione su localhost (ad esempio http://localhost:8000) e quel server di sviluppo verrebbe puntato a una determinata API di ambiente (ad esempio https://testing.api.com o https://staging.api. com) come il test o la messa in scena.
Come mai? A volte abbiamo bisogno di apportare modifiche alla nostra app Web locale, ad esempio aggiungendo selettori più specifici per i nostri test per interagire con gli elementi in un modo più robusto o stavamo sviluppando una nuova funzionalità e avevamo bisogno di regolare e convalidare i test di automazione esistenti passerebbe localmente contro le nostre nuove modifiche al codice. Ogni volta che il codice dell'applicazione è cambiato e non abbiamo ancora eseguito il push-up nell'ambiente distribuito, abbiamo utilizzato questo comando per eseguire i test sulla nostra app Web locale. - Contro un'app distribuita per un determinato ambiente (ad esempio https://testing.app.com o https://staging.app.com) come test o staging
Come mai? Altre volte il codice dell'applicazione non cambia, ma potremmo dover modificare il nostro codice di test per correggere alcune imperfezioni o ci sentiamo abbastanza sicuri da aggiungere o eliminare del tutto i test senza apportare modifiche al frontend. Abbiamo utilizzato ampiamente questo comando per aggiornare o eseguire il debug dei test in locale rispetto all'app distribuita per simulare più da vicino il modo in cui i nostri test vengono eseguiti in CICD. - In esecuzione in un contenitore Docker rispetto a un'app distribuita per un determinato ambiente come test o staging
Come mai? Questo è pensato per CICD in modo che possiamo attivare i test E2E da eseguire in un contenitore Docker rispetto, ad esempio, all'app distribuita di staging e assicurarci che passino prima di distribuire il codice alla produzione o in esecuzioni di test pianificate in una pipeline dedicata. Durante l'impostazione iniziale di questi comandi, abbiamo eseguito molti tentativi ed errori per avviare i contenitori Docker con valori di variabili di ambiente diversi e testare per vedere i test corretti eseguiti con successo prima di collegarlo al nostro provider CICD, Buildkite.
Primo passaggio di configurazioni e script dell'ambiente
Quando abbiamo sperimentato per la prima volta la configurazione di Cypress, l'abbiamo fatto nel repository che copre https://app.sendgrid.com, un'app Web che include pagine di funzionalità come Autenticazione mittente, Attività e-mail e Convalida e-mail, e inevitabilmente abbiamo condiviso le nostre scoperte e le nostre conoscenze con i team dietro la nostra app Web per le campagne di marketing, che comprende il dominio https://mc.sendgrid.com. Volevamo eseguire i nostri test E2E sul nostro ambiente di staging e abbiamo utilizzato l'interfaccia della riga di comando di Cypress e opzioni come --config
o --env
per realizzare i nostri casi d'uso.
Per eseguire i test Cypress sull'app Web localmente su http://127.0.0.1:8000
o sull'URL dell'app di staging distribuita, abbiamo modificato il flag di configurazione baseUrl
nel comando e aggiunto variabili di ambiente aggiuntive come testEnv
per aiutare carica determinati dispositivi o dati di test specifici per l'ambiente nei nostri test Cypress. Ad esempio, le chiavi API utilizzate, gli utenti creati e altre risorse potrebbero essere diverse tra gli ambienti. Abbiamo utilizzato testEnv
per attivare o disattivare quei dispositivi o aggiungere una logica condizionale speciale se alcune funzionalità non erano supportate in un ambiente o se la configurazione del test differiva e avremmo accesso all'ambiente tramite una chiamata come Cypress.env(“testEnv”)
nelle nostre specifiche.
Abbiamo quindi organizzato i nostri comandi cypress:open:*
per rappresentare l'apertura della GUI di Cypress per consentirci di selezionare i nostri test da eseguire attraverso l'interfaccia utente quando abbiamo sviluppato localmente e cypress:run:*
per indicare l'esecuzione dei test in modalità senza testa, che era più personalizzata per l'esecuzione in un contenitore Docker durante CICD. Ciò che viene dopo open
o l' run
sarebbe l'ambiente in modo che i nostri comandi vengano letti facilmente come npm run cypress:open:localhost:staging
per aprire la GUI ed eseguire test su un server di sviluppo Webpack locale che punta alle API di staging o npm run cypress:run:staging
per eseguire test in modalità headless sull'app e sull'API di staging distribuite. Gli script package.json
Cypress sono usciti in questo modo:
Evoluzione delle configurazioni e degli script dell'ambiente
In un altro progetto, abbiamo sviluppato i nostri comandi e configurazioni Cypress per sfruttare alcune logiche Node nel file cypress/plugins/index.js
per avere un file cypress.json
di base e file di configurazione separati che sarebbero stati letti in base a una variabile di ambiente chiamata configFile
per caricare un file di configurazione specifico. I file di configurazione caricati verrebbero quindi uniti al file di base per puntare eventualmente a un server di staging o di finto back-end.
Nel caso ti stia chiedendo di più sul server di backend fittizio, abbiamo sviluppato un server Express con endpoint di backend che restituiscono semplicemente risposte variabili di dati JSON statici e codici di stato (ad esempio 200, 4XX, 5XX) a seconda dei parametri della query passati nelle richieste. Ciò ha sbloccato il frontend per continuare a sviluppare i flussi di pagina con chiamate di rete effettive al server di backend fittizio con risposte che emulano l'aspetto dell'API effettiva quando sarà disponibile in futuro. Potremmo anche simulare facilmente vari livelli di successo e risposte di errore per i nostri diversi stati dell'interfaccia utente che sarebbero altrimenti difficili da riprodurre in produzione, e poiché faremmo chiamate di rete deterministiche, i nostri test Cypress sarebbero meno traballanti nell'attivazione della stessa rete richieste e risposte ogni volta.
Avevamo un file cypress.json
di base che includeva proprietà condivise per timeout generali, ID progetto per collegarsi al servizio Cypress Dashboard di cui parleremo più avanti e altre impostazioni come mostrato di seguito:
Abbiamo creato una cartella di config
nella cartella cypress
per contenere ciascuno dei nostri file di configurazione come localhostMock.json
per eseguire il nostro server di sviluppo Webpack locale su un server API fittizio locale o staging.json
per l'esecuzione contro l'app di staging e l'API distribuite. Questi file di configurazione da differenziare e unire con la configurazione di base erano simili a questo:
I file di configurazione CICD avevano un file JSON ancora più semplice poiché dovevamo impostare le variabili di ambiente in modo dinamico per tenere conto dell'URL di base del front-end del servizio Docker variabile e degli host API del server fittizi che analizzeremo in seguito.
Nel nostro file cypress/plugins/index.js
, abbiamo aggiunto la logica per leggere una variabile d'ambiente chiamata configFile
impostata da un comando Cypress che alla fine avrebbe letto il file corrispondente nella cartella config
e lo avrebbe unito con cypress.json
di base come di seguito:
Per scrivere comandi Cypress sensati con variabili di ambiente impostate per i nostri casi d'uso, abbiamo sfruttato un Makefile
simile al seguente:
Con quei comandi ben disposti in un Makefile, potremmo fare rapidamente cose come make cypress_open_staging
o make cypress_run_staging
nei nostri script npm `package.json`.
In precedenza, eravamo soliti inserire alcuni comandi in una lunga riga che sarebbero stati difficili da modificare senza errori. Per fortuna, il Makefile ha aiutato a distribuire le cose molto meglio con l'interpolazione leggibile delle variabili di ambiente nei comandi Cypress su più righe. Potremmo impostare o esportare rapidamente variabili di ambiente come configFile
per quale file di configurazione dell'ambiente caricare, BASE_URL
per visitare le nostre pagine, API_HOST
per diversi ambienti di back-end o SPECS
per determinare quali test eseguire prima di dare il via a uno qualsiasi dei comandi Makefile.
Abbiamo anche utilizzato i comandi Makefile per altri lunghi script npm e comandi Docker, nonché per creare le nostre risorse Webpack, installare dipendenze o eseguire comandi contemporaneamente ad altri. Alla fine avremmo tradotto alcuni comandi Makefile nella sezione degli script package.json
, sebbene ciò non fosse necessario se qualcuno volesse usare solo il Makefile e sarebbe simile al seguente:
Abbiamo volutamente omesso molti dei comandi CICD di Cypress poiché non erano comandi che sarebbero stati utilizzati nello sviluppo quotidiano e di conseguenza abbiamo mantenuto il package.json
più snello. Ancora più importante, abbiamo potuto vedere subito a colpo d'occhio tutti i comandi Cypress relativi al server fittizio e al server di sviluppo Webpack locale rispetto agli ambienti di staging e quali stanno "aprendo" la GUI anziché "correre" in modalità headless.
Passaggio 3: implementazione dei test E2E in locale
Quando abbiamo iniziato a implementare i test E2E con Cypress, abbiamo fatto riferimento ai nostri test esistenti da WebdriverIO e STUI per la conversione e aggiunto test più recenti per altre funzionalità ad alta priorità, che vanno dai semplici controlli dello stato a complicati flussi di percorso felice. La traduzione degli oggetti pagina esistenti e dei file di test da WebdriverIO o STUI in oggetti pagina e specifiche equivalenti in Cypress si è rivelata un gioco da ragazzi. In realtà ha prodotto un codice molto più pulito rispetto a prima con elementi di attesa meno espliciti e una migliore concatenabilità di asserzioni e altri comandi Cypress.
Ad esempio, i passaggi generali dei test sono rimasti gli stessi dal punto di vista dell'utente finale, quindi il lavoro di conversione ha comportato la mappatura dell'API WebdriverIO o STUI sull'API Cypress nei seguenti modi:
- Molti comandi essenzialmente sono apparsi e hanno funzionato in modo simile al punto in cui stavamo quasi semplicemente sostituendo
$
obrowser
concy
oCypress
, ad esempio visitando una pagina tramite$(“.button”).click()
acy.get(“.button”).click()
,browser.url()
sucy.visit()
o$(“.input”).setValue()
sucy.get(“.input”).type()
- L'utilizzo
$
o$$
di solito si trasforma in uncy.get(...)
ocy.contains(...)
ovvero$$(“.multiple-elements-selector”)
o$(“.single-element-selector”)
trasformato incy.get(“.any-element-selector”)
,cy.contains(“text”)
ocy.contains(“.any-selector”)
- Rimozione di chiamate estranee
$(“.selector”).waitForVisible(timeoutInMs)
,$(“.selector”).waitUntil(...)
o$(“.selector”).waitForExist()
a favore di lasciare Cypress per impostazione predefinita gestire i tentativi e il recupero degli elementi più e più volte concy.get('.selector')
ecy.contains(textInElement)
. Se avessimo bisogno di un timeout più lungo del valore predefinito,cy.get('.selector', { timeout: longTimeoutInMs })
tutto e quindi dopo aver recuperato l'elemento concateneremmo il comando di azione successivo per fare qualcosa con l'elemento, ad esempiocy.get(“.selector”).click()
. - Comandi personalizzati con browser.
addCommand('customCommand, () => {})` turned into `Cypress.Commands.add('customCommand', () => {})
e facendo `cy.customCommand()` - Effettuare richieste di rete per l'installazione o lo smontaggio tramite l'API utilizzando una libreria chiamata
node-fetch
e avvolgerla inbrowser.call(() => return fetch(...))
e/obrowser.waitUntil(...)
ha portato a facendo richieste HTTP in un server Cypress Node tramitecy.request(endpoint)
o un plug-in personalizzato che abbiamo definito ed effettuato chiamate comecy.task(taskToHitAPIOrService)
. - Prima, quando dovevamo attendere che un'importante richiesta di rete potesse terminare senza modifiche significative all'interfaccia utente, a volte dovevamo ricorrere all'utilizzo di
browser.pause(timeoutInMs)
, ma con Cypress l'abbiamo migliorato con la funzionalità di stubbing della rete e siamo stati in grado di ascoltare e attendi che la richiesta specifica termini concy.server()
,cy.route(“method”, “/endpoint/we/are/waiting/for).as(“endpoint”)`, and `cy.wait(“@endpoint”)
prima di avviare l'azione che attiverebbe la richiesta.
Dopo aver tradotto gran parte della sintassi e dei comandi di WebdriverIO in comandi Cypress, abbiamo introdotto lo stesso concetto di avere un oggetto pagina di base per funzionalità condivise comuni e oggetti pagina estesi per ogni pagina richiesta per i test. Ecco un esempio di un oggetto pagina di base con una funzionalità open()
comune da condividere su tutte le pagine.
Un oggetto pagina estesa aggiungerebbe getter per i selettori di elementi, implementerebbe la funzionalità open()
con il relativo percorso di pagina e fornirebbe qualsiasi funzionalità di supporto come mostrato di seguito.
I nostri oggetti di pagina estesi effettivi utilizzavano anche una semplice mappa di oggetti per mantenere tutti i nostri selettori CSS di elementi in un posto che avremmo inserito nei nostri componenti React come attributi di dati, riferimento negli unit test e utilizzato come nostri selettori negli oggetti di pagina Cypress. Inoltre, le nostre classi di oggetti pagina a volte variavano sfruttando il costruttore di classi se, ad esempio, un oggetto pagina veniva riutilizzato per un gruppo di pagine simili dall'aspetto e funzionanti come le nostre pagine Soppressione e passavamo argomenti per modificare il percorso o proprietà specifiche.
Come nota a margine, i team non avevano bisogno di utilizzare oggetti di pagina, ma abbiamo apprezzato la coerenza del modello per mantenere la funzionalità della pagina e i riferimenti del selettore di elementi DOM insieme a una struttura di oggetti di classe standard per condividere funzionalità comuni su tutte le pagine. Altri team preferivano creare molti file diversi con poche funzioni di utilità e senza l'uso di classi ES6, ma l'importante era fornire un modo organizzato e prevedibile per incapsulare tutto e scrivere test per una migliore efficienza e manutenibilità degli sviluppatori.
Abbiamo aderito alla stessa strategia di test generale utilizzata con i nostri vecchi test WebdriverIO tentando di impostare il test il più possibile tramite l'API. Volevamo in particolare evitare di aumentare il nostro stato di configurazione attraverso l'interfaccia utente per non introdurre screpolature e perdite di tempo per le parti che non intendevamo testare. La maggior parte dei test prevedeva questa strategia:
- Configurazione o smontaggio tramite l'API : se fosse necessario testare la creazione di un'entità tramite l'interfaccia utente, ci assicureremmo prima di eliminare l'entità tramite l'API. Indipendentemente dal modo in cui l'esecuzione del test precedente si è conclusa con esito positivo o negativo, il test doveva essere impostato o eliminato correttamente tramite l'API per garantire che il test si comporti in modo coerente e inizi con le giuste condizioni.
- Accesso a un utente di test dedicato tramite l'API – Abbiamo creato utenti di test dedicati per pagina o anche per test di automazione in modo che i nostri test fossero isolati e non calpestassero le risorse l'uno dell'altro quando eseguiti in parallelo. Abbiamo effettuato la stessa richiesta della nostra pagina di accesso tramite l'API e archiviato il cookie prima dell'inizio del test in modo da poter visitare direttamente la pagina autenticata e iniziare le fasi di test effettive.
- Automatizzare i passaggi dal punto di vista dell'utente finale : dopo aver effettuato l'accesso all'utente tramite l'API, abbiamo visitato direttamente la pagina e automatizzato i passaggi che un utente finale avrebbe eseguito per completare un flusso di funzionalità e verificare che l'utente vede e interagisce con le cose giuste lungo la strada.
Per ripristinare il test allo stato originale previsto, accediamo a un utente di test dedicato tramite l'API con un comando cy.login
globale, impostiamo un cookie per mantenere l'accesso dell'utente, effettuiamo le chiamate API necessarie per restituire il utente allo stato iniziale desiderato tramite cy.request(“endpoint”)
o cy.task(“pluginAction”)
e visitare la pagina autenticata che abbiamo cercato di testare direttamente. Quindi, automatizzare i passaggi per realizzare un flusso di funzionalità utente come mostrato nel layout di test di seguito.
Ricordi i comandi personalizzati di cui abbiamo parlato per l'accesso, cy.login()
e per il logout, cy.logout()
? Li abbiamo implementati facilmente in Cypress in questo modo in modo che tutti i nostri test accedano a un utente tramite l'API allo stesso modo.
Inoltre, volevamo automatizzare e verificare determinati flussi complessi che coinvolgono e-mail che prima non potevamo fare bene con WebdriverIO o STUI. Alcuni esempi includevano l'esportazione dell'attività e-mail in un CSV, il flusso di invio a un collega per l'autenticazione del mittente o l'esportazione dei risultati della convalida e-mail in un CSV. Cypress impedisce l'accesso a più superdomini in un test, quindi la navigazione verso un client di posta elettronica tramite un'interfaccia utente che non possediamo era instabile e non un'opzione.
Abbiamo invece sviluppato i plug-in Cypress tramite i loro cy.task(“pluginAction”)
per utilizzare alcune librerie all'interno del server Cypress Node per connettersi a un client/casella di posta IMAP e-mail di prova come SquirrelMail per verificare la corrispondenza delle e-mail in una casella di posta dopo aver richiesto un'azione nell'interfaccia utente e per seguire i collegamenti di reindirizzamento da tali e-mail al dominio della nostra app Web per verificare che alcune pagine di download siano state visualizzate e completare in modo efficace un intero flusso di clienti. Abbiamo implementato plug-in che attendevano l'arrivo delle e-mail nella casella di posta di SquirrelMail in base a determinate righe dell'oggetto, eliminavano e-mail, inviavano e-mail, attivavano eventi e-mail, sondando i servizi di back-end ed eseguendo configurazioni e smontaggi molto più utili tramite l'API per i nostri test da utilizzare.
Per fornire maggiori informazioni su ciò che abbiamo effettivamente testato con Cypress, abbiamo coperto una pletora di casi di alto valore come questi:
- Controlli dello stato di salute per tutte le nostre pagine , note anche come tour dell'app – Volevamo assicurarci che le pagine caricate con alcuni contenuti causassero a volte alcuni servizi di back-end o l'hosting front-end non funzionassero. Si consiglia inoltre di eseguire prima questi test per costruire la memoria muscolare mentale necessaria per creare oggetti di pagina con selettori e funzioni di supporto e per eseguire test rapidi e funzionanti in un ambiente.
- Operazioni CRUD su una pagina : resetteremmo sempre i test di conseguenza tramite l'API e quindi testeremo specificamente la creazione, la lettura, l'aggiornamento o l'eliminazione nell'interfaccia utente. Ad esempio, se abbiamo testato la possibilità di creare un'autenticazione di dominio tramite l'interfaccia utente, indipendentemente da come è terminata l'ultima esecuzione di test, prima di procedere dovevamo assicurarci che il dominio che stavamo per creare tramite l'interfaccia utente fosse stato eliminato tramite l'API con i passaggi automatizzati dell'interfaccia utente per creare il dominio ed evitare collisioni. Se abbiamo verificato la possibilità di eliminare una soppressione tramite l'interfaccia utente, ci siamo assicurati di creare prima la soppressione tramite l'API e quindi di procedere con i passaggi.
- Testare i filtri di ricerca su una pagina – Abbiamo testato l'impostazione di una serie di filtri di ricerca avanzati con Attività e-mail e visitando la pagina con i parametri di ricerca per essere sicuri che i filtri fossero compilati automaticamente. Abbiamo anche aggiunto i dati tramite l'API per la convalida dell'e-mail e ancora una volta abbiamo avviato diversi filtri di ricerca e convalidato la tabella in modo che corrispondesse ai filtri di ricerca in quella pagina.
- Accessi utente diversi : in Twilio SendGrid, abbiamo account padre che possono avere compagni di squadra con ambiti o autorizzazioni di accesso diversi o subutenti sotto di essi che hanno anche vari gradi di accesso e si comportano in qualche modo in modo simile a un account genitore. I compagni di squadra con accesso di sola lettura rispetto a amministratore per determinate pagine e subutenti vedrebbero o non vedrebbero determinate cose su una pagina e ciò ha reso facile automatizzare l'accesso a quei tipi di utenti e controllare ciò che vedono o non vedono nei test di Cypress.
- Pacchetti utente diversi: i nostri utenti possono anche variare tra i tipi di pacchetti gratuiti e a pagamento come Essentials, Pro e Premier e anche quei pacchetti possono vedere o meno determinate cose su una pagina. Accedevamo agli utenti con pacchetti diversi e verificavamo rapidamente le funzionalità, la copia o le pagine a cui gli utenti avevano accesso nei test Cypress.
Passaggio 4: Dockerizzazione dei test
Durante l'esecuzione di ogni passaggio della pipeline Buildkite su una nuova macchina AWS nel cloud, non potevamo semplicemente chiamare npm run cypress:run:staging
perché quelle macchine non hanno Node, browser, il nostro codice dell'applicazione o qualsiasi altra dipendenza per eseguire effettivamente Cypress prove. Quando abbiamo configurato WebdriverIO in precedenza, dovevamo assemblare tre servizi separati in un file Docker Compose per fare in modo che i servizi Selenium, Chrome e del codice dell'applicazione appropriati operassero insieme per l'esecuzione dei test.
Con Cypress, è stato molto più semplice poiché abbiamo richiesto solo l'immagine Docker di base di Cypress, cypress/base
, per configurare l'ambiente in un Dockerfile
e un solo servizio in un file docker-compose.yml
con il nostro codice dell'applicazione per eseguire Cypress prove. Esamineremo un modo per farlo poiché ci sono altre immagini Cypress Docker da utilizzare e altri modi per impostare i test Cypress in Docker. Ti invitiamo a guardare la documentazione di Cypress per un'alternativa
Per aprire un servizio con tutta la nostra applicazione e il codice di test necessari per eseguire i test Cypress, abbiamo creato un Dockerfile
chiamato Dockerfile.cypress
e installato tutti i node_modules
e copiato il codice nella directory di lavoro dell'immagine in un ambiente Node. Questo verrebbe utilizzato dal nostro servizio Docker Compose di cypress
e abbiamo ottenuto la configurazione Dockerfile
nel modo seguente:
Con questo Dockerfile.cypress
, possiamo integrare Cypress per eseguire specifiche selezionate su un determinato ambiente API e app distribuite tramite un servizio Docker Compose chiamato cypress
. Tutto ciò che dovevamo fare era interpolare alcune variabili di ambiente come SPECS
e BASE_URL
per eseguire test Cypress selezionati su un determinato URL di base tramite il npm run cypress:run:cicd:staging
che assomiglia a questo, ”cypress:run:cicd:staging”: “cypress run --record --key --config baseUrl=$BASE_URL --env testEnv=staging”
.
Queste variabili di ambiente verrebbero impostate tramite i file di configurazione/impostazione della pipeline Buildkite o esportate dinamicamente quando si attivano i test Cypress per l'esecuzione dalla nostra pipeline di distribuzione. Un esempio di file docker-compose.cypress.yml
sembrava simile a questo:
Ci sono anche un altro paio di cose da osservare. Ad esempio, puoi vedere la variabile di ambiente VERSION
che ci consente di fare riferimento a un'immagine Docker con tag specifica. Dimostreremo in seguito come taggare un'immagine Docker e quindi tirare verso il basso la stessa immagine Docker affinché quella build venga eseguita sul codice corretto per i test Cypress.
Inoltre, noterai anche il BUILDKITE_BUILD_ID
passato, che viene fornito gratuitamente insieme ad altre variabili di ambiente Buildkite per ogni build che avviamo, e il flag ci-build-id
. Ciò abilita la funzione di parallelizzazione di Cypress e quando impostiamo un certo numero di macchine allocate per i test Cypress, saprà automaticamente come far girare quelle macchine e separare i nostri test per eseguire tutti quei nodi macchina per ottimizzare e accelerare il nostro test tempi di esecuzione.
Alla fine abbiamo anche sfruttato il montaggio del volume e la funzionalità degli artefatti di Buildkite. Carichiamo i video e gli screenshot in modo che siano direttamente accessibili tramite la scheda "Artefatti" dell'interfaccia utente di Buildkite nel caso in cui dovessimo esaurire le nostre registrazioni di prova assegnate a pagamento per il mese o in qualche modo non riuscissimo ad accedere al servizio dashboard. Ogni volta che si esegue il comando Cypress "esegui" in modalità headless, viene visualizzato un output nelle cartelle cypress/videos
e cypress/screenshots
da rivedere localmente e semplicemente montiamo quelle cartelle e le carichiamo su Buildkite per noi come sicurezza.
Passaggio 5: integrazione con CICD
Dopo aver eseguito correttamente i test Cypress in un container Docker in ambienti diversi, abbiamo iniziato a integrarci con Buildkite, il nostro provider CICD. Buildkite ha fornito modi per eseguire passaggi in un file .yml
sulle nostre macchine AWS con script Bash e variabili di ambiente impostate nel codice o tramite le impostazioni della pipeline Buildkite del repository nell'interfaccia utente web. Buildkite ci ha anche consentito di attivare questa pipeline di test dalla nostra pipeline di distribuzione principale con variabili di ambiente esportate e avremmo riutilizzato questi passaggi di test per altre pipeline di test isolate che sarebbero state eseguite in base a una pianificazione per il monitoraggio e l'analisi dei nostri QA.
Ad alto livello, le nostre pipeline Buildkite di test per Cypress e anche le nostre precedenti pipeline WebdriverIO hanno condiviso i seguenti passaggi simili:
- Configura le immagini Docker . Crea, tagga e invia le immagini Docker richieste per i test al registro in modo da poterle estrarre in un passaggio successivo.
- Eseguire i test in base alle configurazioni delle variabili di ambiente . Apri le immagini Docker contrassegnate per la build specifica ed esegui i comandi appropriati su un ambiente distribuito per eseguire suite di test selezionate dalle variabili di ambiente impostate.
Ecco un esempio di un file pipeline.cypress.yml
che mostra la configurazione delle immagini Docker nel passaggio "Crea immagine Docker Cypress" e l'esecuzione dei test nel passaggio "Esegui test Cypress":
Una cosa da notare è il primo passaggio, "Crea immagine Docker Cypress" e come imposta l'immagine Docker per il test. Ha utilizzato il comando Docker Compose build
per creare il servizio cypress
con tutto il codice di test dell'applicazione e lo ha contrassegnato con l' latest
e la variabile di ambiente ${VERSION}
in modo che possiamo eventualmente estrarre la stessa immagine con il tag appropriato per questa build in un passo futuro. Ogni passaggio può essere eseguito su una macchina diversa nel cloud AWS da qualche parte, quindi i tag identificano in modo univoco l'immagine per la specifica esecuzione di Buildkite. Dopo aver taggato l'immagine, abbiamo trasferito l'immagine più recente e contrassegnata con la versione nel nostro registro Docker privato per essere riutilizzata.
Nel passaggio "Esegui test Cypress", estraiamo l'immagine che abbiamo creato, contrassegnato e inserito nel primo passaggio e avviamo il servizio Cypress per eseguire i test. Sulla base di variabili di ambiente come SPECS
e BASE_URL
, eseguiremmo file di test specifici su un determinato ambiente dell'app distribuito per questa build Buildkite specifica. Queste variabili di ambiente verrebbero impostate tramite le impostazioni della pipeline Buildkite o verrebbero attivate dinamicamente da uno script Bash che analizzerebbe un campo di selezione Buildkite per determinare quali suite di test eseguire e in quale ambiente.
Quando selezioniamo quali test eseguire durante la nostra pipeline di distribuzione Buildkite CICD e attiviamo una pipeline di test attivati dedicata con determinate variabili di ambiente esportate, seguiamo i passaggi nel file pipeline.cypress.yml
per realizzarlo. Un esempio di attivazione dei test dopo la distribuzione di un nuovo codice in un ambiente di branch di funzionalità dalla pipeline di distribuzione è simile al seguente:
I test attivati verrebbero eseguiti in una pipeline separata e, dopo aver seguito il collegamento "Build #639", ci porterebbe ai passaggi di compilazione per l'esecuzione del test attivato come di seguito:
Riutilizzando lo stesso file pipeline.cypress.yml
per le nostre pipeline Cypress Buildkite dedicate in esecuzione in base a una pianificazione, abbiamo build come quella che esegue i nostri test E2E "P1", con la massima priorità, come mostrato nella foto sotto:
Tutto quello che dobbiamo fare è impostare le variabili di ambiente corrette per cose come quali specifiche eseguire e quale ambiente di back-end per colpire nelle impostazioni della pipeline Buildkite. Quindi, possiamo configurare una build pianificata di Cron, che è anche nelle impostazioni della pipeline, per iniziare ogni certo numero di ore e siamo a posto. We would then create many other separate pipelines for specific feature pages as needed to run on a schedule in a similar way and we would only vary the Cron schedule and environment variables while once again uploading the same `pipeline.cypress.yml` file to execute.
In each of those “Run Cypress tests” steps, we can see the console output with a link to the recorded test run in the paid Dashboard Service, the central place to manage your team's test recordings, billing, and other Cypress stats. Following the Dashboard Service link would take us to a results view for developers and QAs to take a look at the console output, screenshots, video recordings, and other metadata if required such as this:
Step 6: Comparing Cypress vs. WebdriverIO/STUI
After diving into our own custom Ruby Selenium solution in STUI, WebdriverIO, and finally Cypress tests, we recorded our tradeoffs between Cypress and Selenium wrapper solutions.
Professionisti
- It's not another Selenium wrapper – Our previous solutions came with a lot of Selenium quirks, bugs, and crashes to work around and resolve, whereas Cypress arrived without the same baggage and troubles to deal with in allowing us full access to the browser.
- More resilient selectors – We no longer had to explicitly wait for everything like in WebdriverIO with all the
$(.selector).waitForVisible()
calls and now rely oncy.get(...)
and cy.contains(...)
commands with their default timeout. It will automatically keep on retrying to retrieve the DOM elements and if the test demanded a longer timeout, it is also configurable per command. With less worrying about the waiting logic, our tests became way more readable and easier to chain. - Vastly improved developer experience – Cypress provides a large toolkit with better and more extensive documentation for assertions, commands, and setup. We loved the options of using the Cypress GUI, running in headless mode, executing in the command-line, and chaining more intuitive Cypress commands.
- Significantly better developer efficiency and debugging – When running the Cypress GUI, one has access to all of the browser console to see some helpful output, time travel debug and pause at certain commands in the command log to see before and after screenshots, inspect the DOM with the selector playground, and discern right away at which command the test failed. In WebdriverIO or STUI we struggled with observing the tests run over and over in a browser and then the console errors would not point us toward and would sometimes even lead us astray from where the test really failed in the code. When we opted to run the Cypress tests in headless mode, we got console errors, screenshots, and video recordings. With WebdriverIO we only had some screenshots and confusing console errors. These benefits resulted in us cranking out E2E tests much faster and with less overall time spent wondering why things went wrong. We recorded it took less developers and often around 2 to 3 times less days to write the same level of complicated tests with Cypress than with WebdriverIO or STUI.
- Network stubbing and mocking – With WebdriverIO or STUI, there was no such thing as network stubbing or mocking in comparison to Cypress. Now we can have endpoints return certain values or we can wait for certain endpoints to finish through
cy.server()
andcy.route()
. - Less time to set up locally – With WebdriverIO or STUI, there was a lot of time spent up front researching which reporters, test runners, assertions, and services to use, but with Cypress, it came bundled with everything and started working after just doing an
npm install cypress.
- Less time to set up with Docker – There are a bunch of ways to set up WebdriverIO with Selenium, browser, and application images that took us considerably more time and frustration to figure out in comparison to Cypress's Docker images to use right out of the gate.
- Parallelization with various CICD providers – We were able to configure our Buildkite pipelines to spin up a certain number of AWS machines to run our Cypress tests in parallel to dramatically speed up the overall test run time and uncover any flakiness in tests using the same resources. The Dashboard Service would also recommend to us the optimal number of machines to spin up in parallel for the best test run times.
- Paid Dashboard Service – When we run our Cypress tests in a Docker container in a Buildkite pipeline during CICD, our tests are recorded and stored for us to look at within the past month through a paid Dashboard Service. We have a parent organization for billing and separate projects for each frontend application to check out console output, screenshots, and recordings of all of our test runs.
- Tests are way more consistent and maintainable – Tests passed way more consistently with Cypress in comparison to WebdriverIO and STUI where the tests kept on failing so much to the point where they were often ignored. Cypress tests failing more often signaled actual issues and bugs to look into or suggested better ways to refactor our tests to be less flaky. With WebdriverIO and STUI, we wasted a lot more time in maintaining those tests to be somewhat useful, whereas with Cypress, we would every now and then adjust the tests in response to changes in the backend services or minor changes in the UI.
- Tests are faster – Builds passed way more consistently and overall test run times would be around 2 to 3 times faster when run serially without parallelization. We used to have overall test runs that would take hours with STUI and around 40 minutes with WebdriverIO, but now with way more tests and with the help of parallelization across many machine nodes, we can run over 200 tests in under 5 minutes .
- Room to grow with added features in the future – With a steady open-source presence and dedicated Cypress team working towards releasing way more features and improvements to the Cypress infrastructure, we viewed Cypress as a safer bet to invest in rather than STUI, which would require us to engineer and solve a lot of the headaches ourselves, and WebdriverIO, which appeared to feel more stagnant in new features added but with the same baggage as other Selenium wrappers.
contro
- Lack of cross-browser support – As of this writing, we can only run our tests against Chrome. With WebdriverIO, we could run tests against Chrome, Firefox, Safari, and Opera. STUI also provided some cross-browser testing, though in a much limited form since we created a custom in-house solution
- Cannot integrate with some third-party services – With WebdriverIO, we had the option to integrate with services like BrowserStack and Sauce Labs for cross-browser and device testing. However, with Cypress there are no such third-party integrations but there are some plugins with services like Applitools for visual regression testing available. STUI, on the other hand, also had some small integrations with TestRail , but as a compromise, we log out the TestRail links in our Cypress tests so we can refer back to them if we needed to.
- Requires workarounds to test with iframes – There are some issues around handling iframes with Cypress. We ended up creating a global Cypress command to wrap how to deal with retrieving an iframe's contents as there is no specific API to deal with iframes like how WebdriverIO does.
To summarize our STUI, WebdriverIO, and Cypress comparison, we analyzed the overall developer experience (related to tools, writing tests, debugging, API, documentation, etc.), test run times, test passing rates, and maintenance as displayed in this table:
Following our analysis of the pros and cons of Cypress versus our previous solutions, it was pretty clear Cypress would be our best bet to accomplish our goal of writing fast, valuable, maintainable, and debuggable E2E tests we could integrate with CICD.
Though Cypress lacked features such as cross-browser testing and other integrations with third-party services that we could have had with STUI or WebdriverIO, we most importantly need tests that work more often than not and with the right tools to confidently fix broken ones. If we ever needed cross-browser testing or other integrations we could always still circle back and use our knowledge from our trials and experiences with WebdriverIO and STUI to still run a subset of tests with those frameworks.
We finally presented our findings to the rest of the frontend organization, engineering management, architects, and product. Upon demoing the Cypress test tools and showcasing our results between WebdriverIO/STUI and Cypress, we eventually received approval to standardize and adopt Cypress as our E2E testing library of choice for our frontend teams.
Step 7: Scaling to Other Frontend Teams
After successfully proving that using Cypress was the way to go for our use cases, we then focused on scaling it across all of our frontend teams' repos. We shared lessons learned and patterns of how to get up and running, how to write consistent, maintainable Cypress tests, and of how to hook those tests up during CICD or in scheduled Cypress Buildkite pipelines.
To promote greater visibility of test runs and gain access to a private monthly history of recordings, we established our own organization to be under one billing method to pay for the Dashboard Service with a certain recorded test run limit and maximum number of users in the organization to suit our needs.
Once we set up an umbrella organization, we invited developers and QAs from different frontend teams and each team would install Cypress, open up the Cypress GUI, and inspect the “Runs” and “Settings” tab to get the “Project ID” to place in their `cypress.json` configuration and “Record Key” to provide in their command options to start recording tests to the Dashboard Service. Finally, upon successfully setting up the project and recording tests to the Dashboard Service for the first time, logging into the Dashboard Service would show that team's repo under the “Projects” tab like this:
When we clicked a project like “mako”, we then had access to all of the test runs for that team's repo with quick access to console output, screenshots, and video recordings per test run upon clicking each row as shown below:
For more insights into our integration, we set up many separate dedicated test pipelines to run specific, crucial page tests on a schedule like say every couple hours to once per day. We also added functionality in our main Buildkite CICD deploy pipeline to select and trigger some tests against our feature branch environment and staging environment.
Come ci si poteva aspettare, questo ha rapidamente spazzato via i nostri test registrati assegnati per il mese, soprattutto perché ci sono più team che contribuiscono e attivano i test in vari modi. Per esperienza, si consiglia di prestare attenzione a quanti test vengono eseguiti in base a una pianificazione, alla frequenza con cui vengono eseguiti questi test e a quali test vengono eseguiti durante CICD. Potrebbero esserci alcune esecuzioni di test ridondanti e altre aree per essere più frugali, come ridurre la frequenza delle esecuzioni di test programmate o eventualmente eliminarne alcune del tutto per attivare i test solo durante il CICD.
La stessa regola dell'essere frugali si applica all'aggiunta di utenti, come abbiamo sottolineato, fornendo l'accesso solo agli sviluppatori e ai QA nei team di frontend che utilizzeranno pesantemente il servizio Dashboard piuttosto che i vertici e altre persone al di fuori di quei team per riempire quei posti limitati.
Quello che non vediamo l'ora con Cypress
Come accennato in precedenza, Cypress ha dimostrato molte promesse e potenziale di crescita nella comunità open source e con il suo team dedicato incaricato di fornire funzionalità più utili da utilizzare con i nostri test E2E. Molti degli aspetti negativi che abbiamo evidenziato vengono attualmente affrontati e non vediamo l'ora di vedere cose come:
- Supporto cross-browser – Questo è un grosso problema perché gran parte del nostro rifiuto di adottare Cypress è venuto dall'utilizzo del solo Chrome rispetto alle soluzioni basate su Selenium, che supportavano browser come Firefox, Chrome e Safari. Per fortuna, test più affidabili, manutenibili e di cui è possibile eseguire il debug hanno vinto per la nostra organizzazione e speriamo di potenziare le nostre suite di test con più test cross-browser in futuro quando il team di Cypress rilascerà tale supporto cross-browser.
- Riscrittura del livello di rete : anche questa è enorme poiché tendiamo a utilizzare pesantemente l'API Fetch nelle nostre aree React più recenti e nelle aree di applicazione Backbone/Marionette più vecchie usavamo ancora jQuery AJAX e normali chiamate basate su XHR. Possiamo facilmente escludere o ascoltare le richieste nelle aree XHR, ma abbiamo dovuto escogitare alcune soluzioni hacky con i polyfill di recupero per ottenere lo stesso effetto. La riscrittura del livello di rete dovrebbe aiutare ad alleviare questi problemi.
- Miglioramenti incrementali al servizio dashboard: di recente abbiamo già visto alcune nuove modifiche all'interfaccia utente del servizio dashboard e speriamo di continuare a vederlo crescere con più visualizzazioni statistiche e analisi di dati utili. Utilizziamo anche molto la funzione di parallelizzazione e controlliamo spesso le nostre registrazioni di test non riuscite nel servizio dashboard, quindi sarebbe bello vedere eventuali miglioramenti iterativi al layout e/o alle funzionalità.
Adozione del cipresso nel futuro
Per la nostra organizzazione, abbiamo apprezzato l'efficienza, il debug e la stabilità degli sviluppatori dei test Cypress eseguiti con il browser Chrome. Abbiamo ottenuto test coerenti e preziosi con meno manutenzione lungo la strada e con molti strumenti per sviluppare nuovi test e correggere quelli esistenti.
Nel complesso, la documentazione, l'API e gli strumenti a nostra disposizione hanno superato di gran lunga qualsiasi svantaggio. Dopo aver sperimentato la GUI Cypress e il servizio dashboard a pagamento, non volevamo assolutamente tornare a WebdriverIO o alla nostra soluzione Ruby Selenium personalizzata.
Abbiamo collegato i nostri test a Buildkite e raggiunto il nostro obiettivo di fornire un modo per scrivere test di automazione E2E coerenti, eseguibili con il debug, manutenibili e di valore per le nostre applicazioni front-end da integrare con CICD . Abbiamo dimostrato con prove al resto dei team di frontend, ai vertici ingegneristici e ai proprietari di prodotti i vantaggi dell'adozione di Cypress e dell'eliminazione di WebdriverIO e STUI.
Centinaia di test successivi tra i team frontend in Twilio SendGrid e abbiamo rilevato molti bug nel nostro ambiente di staging e siamo stati in grado di correggere rapidamente qualsiasi test traballante dalla nostra parte con molta più sicurezza che mai. Gli sviluppatori e i QA non temono più l'idea di scrivere test E2E, ma ora non vedono l'ora di scriverli per ogni singola nuova funzionalità che rilasceremo o per ogni funzionalità precedente che potrebbe utilizzare un po' più di copertura.