TypeScript Toate lucrurile din testele tale Cypress #frontend@twiliosendgrid

Publicat: 2020-11-14

La Twilio SendGrid, scriem majoritatea aplicațiilor noastre web frontend, în special pagini și funcții noi, cu TypeScript și React astăzi pentru o mai bună verificare a tipului, mentenanță și documentare a bazei noastre de cod. Când am început să scriem teste Cypress în urmă cu peste doi ani, majoritatea obiectelor paginii noastre, asistențelor și fișierelor de specificații erau încă implementate în JavaScript și am fost în mare parte pe Cypress versiunea 3.xx Testele mai recente au fost deja scrise în TypeScript, dar noi avea încă un număr mare de fișiere de convertit și migrat către TypeScript.

Am vrut să profităm de beneficiile introducerii pe deplin a componentelor noastre, a testelor unitare și a testelor Cypress E2E . Ceea ce a făcut procesul mai ușor a fost migrarea la o versiune mai nouă de Cypress pentru a profita de suportul TypeScript din cutie începând cu Cypress 4.4.0.

Dacă doriți să faceți un pas înapoi și să aflați mai general cum să vă gândiți la scrierea testelor E2E, nu ezitați să consultați această postare de blog. Dacă doriți, de asemenea, să vedeți o prezentare generală de o mie de picioare a celor mai comune lucruri pe care le-am folosit sau le-am făcut înainte cu scrierea testelor Cypress în diferite medii, puteți consulta această postare de blog înainte de a începe să adăugați TypeScript la testele Cypress. Această postare de blog presupune că sunteți familiarizat cu testele Cypress și că ați mai folosit API-ul lor.

Dacă sunteți gata să vedeți cum am scris lucrurile, să ne uităm mai întâi la câteva modificări inițiale, cum ar fi adăugarea suportului TypeScript la Cypress dacă veniți de la o versiune mai veche.

Migrarea de la Cypress 3.x la >= 4.4.0

Pentru cei care și-au configurat deja infrastructura Cypress pentru a funcționa cu TypeScript în versiunile Cypress 3.xx până la 4.4.3, cel mai probabil ați experimentat unele încercări și erori în configurarea corectă a preprocesorului Webpack în plugins/index.js . Pentru echipa Twilio SendGrid, au existat unele probleme cu anumite fișiere care trebuiau să fie fișiere JavaScript în plugins și foldere de support , iar erorile Cypress greu de depanat ar apărea. Odată ce ai ceva de lucru, ar trebui să arate cam așa.


După actualizarea de la o versiune anterioară a 3.xx, am reușit să renunțăm la configurația preprocesorului Webpack, să înlocuim fișierul plugins/index.js cu un fișier index.ts , să schimbăm puțin cu tsconfig.json din folderul Cypress și, în final, folosind fișiere TS în folderul nostru Cypress (cum ar fi some_page.spec.ts , index.d.ts sau page_object.ts ) – nu mai există configurația preprocesorului Webpack și pur și simplu a funcționat! Am fost mulțumiți de cât de mult mai curat și mai frumos a fost să nu vă gestionați propria configurație a preprocesorului Webpack și să aveți o acoperire mai bună TypeScript asupra fișierelor noastre, așa cum se arată mai jos.

Cu suportul TypeScript acoperit, ne-am uitat apoi la exemplul de aplicație din lumea reală a echipei Cypress, repo-ul cypress-real-world-app, pentru a afla mai multe despre cum să tastați mai bine lucrurile. Am descoperit cum să tastați cy.task(“pluginName”, { … }) și Cypress.env(“someEnvVar”) pentru o mai bună înlănțuire și suport pentru tip intellisense la actualizarea fișierelor. Am săpat și prin documentația TypeScript însoțitoare. Acest lucru ne-a învățat cum să introducem lucruri precum comanda noastră personalizată cy.login() și cum să setăm un fișier de configurare tsconfig.json . Aplicația exemplu are, de asemenea, un tsconfig.json pe care îl puteți consulta, care vă poate oferi o configurație TypeScript de bază excelentă pe care să o personalizați în funcție de preferințele dvs. Vă recomandăm să fiți la curent cu cele mai recente versiuni Cypress, să vă scufundați în resursele oficiale Cypress și să experimentați cu fișierele de definire a tipului dvs.

Tastarea comenzilor personalizate

Am creat câteva comenzi personalizate globale, cum ar fi cy.login() pentru a gestiona autentificarea prin API, astfel încât să poată fi reutilizată în toate fișierele noastre cu specificații. Iată un exemplu despre cum vă puteți introduce propria comandă personalizată de conectare, având în vedere datele de conectare ale utilizatorului și returnând un token de autentificare.

Tastarea variabilelor de mediu

Pentru a face față mai multor medii de testare, cum ar fi dev și staging, am profitat de configurarea variabilelor de mediu, cum ar fi: „testEnv” pentru a păstra mediul în care testăm în prezent, cum ar fi staging, „apiHost” pentru a păstra gazda API-ului backend și alte variabile. Puteți introduce apelurile dvs. Cypress.env() pentru a tasta mai bine funcțiile și alte obiecte care se bazează pe utilizarea acelor valori ale variabilelor de mediu ca aceasta.

Pluginuri de tastare

Am creat multe funcții de plugin cy.task() pentru a gestiona lucruri de la apelurile API, sondarea pentru servicii și verificarea corespondenței e-mailurilor în căsuțele de e-mail de testare. Anterior, la înlănțuirea oricăruia dintre aceste apeluri de funcție, cum ar fi cy.task().then((data) => {}) , subiectul de date înlănțuit era tastat ca any sau unknown , ceea ce nu era grozav pentru fișierele noastre TypeScript. Am descoperit prin exemplele Cypress cum să introducem mai bine pluginurile pe baza numelui pluginului și a argumentelor transmise în apelurile de funcție. Acest lucru a permis fișierelor noastre TypeScript să detecteze care ar fi tipul de date înlănțuit.

O problemă subtilă pe care am întâlnit-o a fost că numele și argumentele pluginului trebuie să se potrivească exact cu modul în care l-ați introdus. Am constatat că este important să plasați cursorul peste tipul .then( .then() înlănțuit și obiectul argument cy.task() din editorul dvs. pentru a se dubla. verificați dacă tipurile se potrivesc corect. Uneori, dacă utilizați un subiect înlănțuit din altă funcție Cypress, cum ar fi cy.getCookie(“auth_token”).its(“value”).then((token) => { }) sau cy.wrap(data).then((data) => {}) , trebuie să tastați și acele argumente de date înlănțuite înainte de a le transmite ca argument funcție cy.task(..., { token, data }) , altfel veți vedea în continuare cy.task(...).then((data) => { }) data part tastat ca any sau unknown . Este mai bine pentru tine să fii mai explicit în multe dintre tipurile de funcții Cypress înlănțuite, cum ar fi .its(“value”).then((token: string) => {}) sau cy.wrap(data).then((data: DataType) => {}) înainte de a le transmite ca parte a obiectului de argumente cy.task() pentru a vă asigura că tipurile funcționează din nou.

Am creat fișiere separate de tip plugin Typescript care ar exporta funcții pentru a fi folosite înapoi în plugins/index.ts . Pe măsură ce numărul de pluginuri crește, vă recomandăm să organizați implementările acestor funcții de plugin în funcție de pagină sau caracteristică, pentru a menține fișierul plugins/index.ts mic. Ar trebui să fie mai ușor de citit dintr-o privire, unde definiți toate funcțiile dvs. cy.task(...) în fișierul plugins/index.ts . În sfârșit, puteți introduce aceste funcții de activitate în index.d.ts în felul următor:

Punerea totul împreună într-un fișier de declarație de tip

Am plasat toate tipurile noastre pentru comenzile noastre personalizate, variabilele de mediu și pluginurile într-un fișier index.d.ts din folderul de support . Vă recomandăm să plasați toate tipurile de Cypress într-un fișier principal de definiție TypeScript pentru a menține lucrurile organizate. Pentru a ocoli tipurile lipsă din dependențele externe utilizate în codul dvs. de testare Cypress, puteți defini și fișiere de modul cum ar fi „some-lib.d.ts”, care ar include declare module 'some-lib' , pentru a rezolva avertismentele TypeScript ale bibliotecii. Puteți chiar să utilizați caracteristica TypeScript de import pentru a introduce tipuri/interfețe definite în celelalte fișiere plugin/utils pentru a evita duplicarea definițiilor de tip în mai multe fișiere. Puteți adăuga aceste tipuri într-un spațiu de nume Cypress și le puteți organiza în felul următor:

Tastarea obiectelor dispozitivului de testare, obiectelor pagină și fișierelor cu specificații

Când dorim să încărcăm diferiți utilizatori și metadate pentru un mediu de testare, am ilustrat anterior cum putem combina variabilele de mediu precum „testEnv” cu valorile „testare” sau „staging” pentru a extrage „testare” sau „ punere în scenă” obiecte din obiectul de ansamblu al dispozitivelor de testare. Puteți introduce aceste obiecte de mediu pentru dispozitive de testare cu generice pentru o structură coerentă pentru toate specificațiile dvs. de partajat. Pentru fiecare mediu de testare, puteți avea aceleași acreditări de utilizator și meta câmpuri folosind un tip generic pentru un test pentru a adăuga atâtea proprietăți câte este nevoie. Vezi exemplul de mai jos.

Tastarea obiectelor paginii și introducerea fișierelor de specificații corespunzătoare depind de comenzile, pluginurile și alte utilitare Cypress pe care le utilizați. În cea mai mare parte, introducerea obiectelor de pagină sau a fișierelor cu specificații nu necesită multe modificări de la omologii lor JavaScript (presupunând că ați introdus deja pluginurile și apelurile variabilelor de mediu). Ocazional, o funcție de ajutor pentru obiectul paginii pe care ați definit-o poate avea nevoie de tastarea unor argumente sau răspunsul care vine de la un apel cy.request() poate fi nevoie să fie tastat cu as atare response.body as SomeType . În general, editorul dvs., cum ar fi VSCode, poate detecta automat tipurile înlănțuite ale apelurilor dvs. cy.task() sau cy.customCommand() fără a fi nevoie să adăugați mai multe tipuri în fișierele de specificații pentru a compensa orice avertismente TypeScript.

Iată un exemplu de părți ale unui obiect pagină cu unele funcții de ajutor și un fișier cu specificații care utilizează obiectul pagină, comanda personalizată de conectare și sarcinile plugin.

Concluzie

Adăugarea TypeScript la testele noastre Cypress ne-a ajutat să evităm erorile și ne-a îmbunătățit experiența dezvoltatorilor atunci când scriem teste Cypress. Când folosim cy.task() , Cypress.env() și cy.customCommand() , putem obține o verificare mai bună a tipului asupra argumentelor și ieșirilor funcției, precum și a profita de completarea codului în IDE-ul nostru, cum ar fi VSCode.

Cheia este să creați propriul fișier de declarație de tip, cum ar fi un fișier index.d.ts , în care puteți suprascrie sau extinde interfețele „Cypress” și „Chainable” pe baza comenzilor personalizate, variabilelor de mediu și funcțiilor plugin-ului de activitate pe care le aveți folosind. În fișierele TypeScript obiect și specificații ale paginii, puteți utiliza apoi acele funcții Cypress și treceți cu mouse-ul peste sau urmați definiția tipurilor de intrare și ieșire așteptate.

Mai mult decât atât, încercați să utilizați TypeScript cu Cypress, deoarece are suport complet pentru TypeScript în versiunile sale mai recente. Testați dacă vă ajută să documentați mai clar funcțiile și să evitați utilizarea incorectă a API-ului. Testele dvs. TypeScript vor arăta probabil în continuare similare cu testele JavaScript Cypress, astfel încât să puteți converti în mod constant unele teste la un moment dat pentru a compara și contrasta abordările.

Dacă sunteți interesat de mai multe postări legate de ceea ce am învățat din testele noastre Cypress, iată câteva articole pe care ar trebui să le consultați:

  • Ce să țineți cont atunci când scrieți teste E2E
  • Prezentare generală de 1.000 de picioare despre scrierea testelor de chiparoși
  • Confruntarea cu fluxurile de e-mail în testele Cypress
  • Idei pentru configurarea, organizarea și consolidarea testelor dvs. de Cypress
  • Integrarea testelor Cypress cu Docker, Buildkite și CICD