Testarea componentelor în reacție utilizând jest și enzime

Aceasta este a doua parte a seriei privind Componentele de testare în React. Dacă aveți experiență anterioară cu Jest, puteți săriți și utilizați codul GitHub ca punct de plecare. 

În articolul precedent, am abordat principiile de bază și ideile care stau la baza dezvoltării bazate pe teste. De asemenea, am creat mediul și instrumentele necesare pentru executarea testelor în React. Setul de instrumente include Jest, ReactTestUtils, Enzyme și reacter-test-renderer. 

Apoi am scris câteva teste pentru o aplicație demo folosind ReactTestUtils și am descoperit neajunsurile sale în comparație cu o bibliotecă mai robustă, cum ar fi enzima.

În acest post, vom obține o înțelegere mai profundă a componentelor de testare din React, scriind mai multe teste practice și realiste. Puteți să mergeți la GitHub și să clonați repo-ul meu înainte de a începe.

Noțiuni de bază cu Enzyme API

Enzyme.js este o bibliotecă open-source întreținută de Airbnb și este o resursă excelentă pentru dezvoltatorii React. Utilizează sub API ReactTestUtils API, dar spre deosebire de ReactTestUtils, Enzyme oferă un API de nivel înalt și o sintaxă ușor de înțeles. Instalați Enzima dacă nu ați făcut-o deja.

Enzyme API exportă trei tipuri de opțiuni de randare:

  1. adâncime redusă
  2. interpretarea DOM completă
  3. redare statică

Redarea redusă este folosit pentru a face o componentă particulară în izolare. Componentele copilului nu vor fi redate și, prin urmare, nu veți putea să-i afirmați comportamentul. Dacă te vei concentra pe testele unității, îți va plăcea asta. Aveți posibilitatea de a face superficial o componentă de genul:

import superficial de la "enzimă"; import ProductHeader de la './ProductHeader'; Exemplul mai concret de mai jos. const component = adâncime (); 

Vizualizare completă DOM generează un DOM virtual al componentei cu ajutorul unei biblioteci numită jsdom. Puteți beneficia de această caracteristică prin înlocuirea funcției superficial() metoda cu montură() în exemplul de mai sus. Beneficiul evident este că puteți face și componentele copilului. Dacă doriți să testați comportamentul unei componente cu copiii săi, ar trebui să utilizați acest lucru. 

Analiza statică este folosit pentru a face componentele să reacționeze la static HTML. Este implementat folosind o bibliotecă numită Cheerio și puteți citi mai multe despre aceasta în docs. 

Revizuirea testelor noastre anterioare

Iată testele pe care le-am scris în ultimul tutorial:

src / componente / __ __ Teste / ProductHeader.test.js

import ReactTestUtils de la "react-dom / test-utils"; // ES6 descriu ('Componentă ProductHeader', () => it ('are o etichetă h2', () => const component = ReactTestUtils .renderIntoDocument); var node = ReactTestUtils .findRenderedDOMComponentWithTag (componenta, 'h2'); ); ('are o clasă de titlu', () => const component = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithClass (component, 'title'); ))

Primul test verifică dacă ProducerHeader componenta are un

tag-ul, iar cel de-al doilea detectează dacă are o clasă CSS numită titlu. Codul este greu de citit și de înțeles. 

Iată testele rescrise folosind Enzima.

src / componente / __ __ Teste / ProductHeader.test.js

import shallow de la 'enzime' descrie ('Componentă ProductHeader', () => it ('are o etichetă h2'); var node = component.find ('h2'); se așteaptă (node.length) .toEqual (1); ); ('are o clasă de titlu', () => const component = superficial (); var node = component.find ('h2'); se așteaptă (node.hasClass ( 'titlu')) toBeTruthy ().; ))

În primul rând, am creat un DOM prea puțin redat  componente folosind superficial() și a păstrat-o într-o variabilă. Apoi, am folosit .găsi() metodă pentru a găsi un nod cu eticheta "h2". Se întreabă DOM pentru a vedea dacă există un meci. Deoarece există doar o instanță a nodului, putem presupune în siguranță acest lucru node.length va fi egal cu 1.

Al doilea test este foarte asemănător celui dintâi. hasClass ( 'titlu') metoda returnează dacă nodul curent are a numele clasei propunere cu valoare "titlu". Putem verifica veridicitatea folosind toBeTruthy ().  

Rulați testele folosind test de fire, și ambele teste ar trebui să treacă. 

Foarte bine! Acum este timpul să refaceți codul. Acest lucru este important din perspectiva unui tester, deoarece testele lizibile sunt mai ușor de întreținut. În testele de mai sus, primele două linii sunt identice pentru ambele teste. Le puteți refactor folosind o beforeEach () funcţie. După cum sugerează și numele, beforeEach funcția este chemată o dată înainte de fiecare spec într-un bloc de descriere este executat. 

Puteți trece o funcție de săgeată beforeEach () asa.

src / componente / __ __ Teste / ProductHeader.test.js

importul shallow de la "enzime" descrie ('Componentă ProductHeader', () => let componenta, nod; // Jest înainteEach () beforeEach (() => component =))) înainte deEach (() => node = component.find ('h2'))) ('are o etichetă h2', () => așteptare (nod) toBeTruthy ()); (') are o clasă de titlu', () => așteptați (node.hasClass ('title')) toBeTruthy ())

Scrierea unităților de testare cu jest și enzime

Să scriem câteva teste unitare pentru Detalii produs componentă. Este o componentă de prezentare care afișează detaliile fiecărui produs individual. 

Vom testa secțiunea care este evidențiată

Testul unității va încerca să afirme următoarele ipoteze:

  • Componenta există și recuzita se transmite.
  • Reclamele cum ar fi numele produsului, descrierea și disponibilitatea sunt afișate.
  • Se afișează un mesaj de eroare atunci când recuzele sunt goale.

Aici este structura oaselor goale a testului. Primul beforeEach () stochează datele despre produs într-o variabilă, iar cea de-a doua montează componenta.

src / componente / __ __ Teste / ProductDetails.test.js

descrie ("componenta ProductDetails", () => var component, product; beforeEach (() => product = id: 1, 'Disponibil';) înainteEach (() => component = mount (); ) aceasta ('test # 1', () => ))

Primul test este ușor:

aceasta ar trebui să existe ('ar trebui să existe', () => așteptați (componentă). toBeTruthy (); așteptați (component.props () produs) toEqual (product)

Aici folosim recuzită() care este utilă pentru obținerea elementelor de recuzită ale unei componente.

Pentru al doilea test, puteți interoga elemente după numele claselor lor și apoi verificați dacă numele produsului, descrierea etc. fac parte din elementul respectiv innerText

 ("ar trebui să afișeze datele despre produs atunci când sunt depuse props", () => let title = component.find ('.product-title'); așteptați (title.text ()). description = component.find ("descrierea produsului"); așteptați (description.text ()) toEqual (product.description);) 

text() este utilă în acest caz în special pentru a prelua textul interior al unui element. Încercați să scrieți o așteptare pentru product.status () și să vedem dacă trec toate testele.

Pentru testul final, o să montăm Detalii produs fără componente. Apoi vom căuta o clasă numită ".product-error" și vom verifica dacă conține textul "Ne pare rău, produsul nu există".

 ("ar trebui să afișeze o eroare atunci când recuzită nu sunt trecute", () => / * component fără props * / component = mount (); permiteți node = component.find ('. product-error'); așteptați (node.text ()). toEqual ("Ne pare rău, produsul nu există"); ) 

Asta e. Am testat cu succes componentă în izolare. Încercările de acest tip sunt cunoscute ca teste unitare.

Testarea apelurilor de tip spate folosind stubs și spioni

Tocmai am învățat cum să testați recuzita. Dar, pentru a testa cu adevărat o componentă izolată, trebuie de asemenea să testați funcțiile de apel invers. În această secțiune, vom scrie teste pentru Lista de produse componentă și să creeze puncte pentru funcțiile de apel invers de-a lungul drumului. Iată ipotezele pe care trebuie să le afirmăm.

  1. Numărul de produse enumerate ar trebui să fie echivalent cu numărul de obiecte pe care componenta le primește ca elemente de recuzită.
  2. Dând clic pe ar trebui să invocați funcția de apel invers.

Să creăm a beforeEach () funcția care completează datele de produs falsificate pentru testele noastre.

src / componente / __ __ Teste / ProductList.test.js

 înainte de toate (=) productData = [id: 1, nume: 'Nike Liteforce Blue Adidas', descriere: 'Lorem ipsu.', status: 'Available'

Acum, să punem componenta în altul beforeEach () bloc.

înainteEach () => handleProductClick = jest.fn (); component = mount (  ); )

Lista de produse primește datele produsului prin recuzită. În plus, acesta primește un apel invers de la părinte. Deși ați putea scrie teste pentru funcția de apel invers a părintelui, nu este o idee grozavă dacă obiectivul dvs. este de a rămâne la teste unitare. Deoarece funcția de apel invers aparține componentei părinte, încorporarea logicii părintelui va face ca testele să fie complicate. În schimb, vom crea o funcție stub.

Ce este un stub? 

Un stub este o funcție falsă care pretinde a fi o altă funcție. Acest lucru vă permite să testați independent o componentă fără a importa componente părinte sau copil. În exemplul de mai sus, am creat o funcție stub numită handleProductClick invocând jest.fn ()

Acum trebuie doar să găsim toate elemente din DOM și simulează un clic pe prima nodul. După ce a fost făcut clic, vom verifica dacă handleProductClick () a fost invocată. Dacă da, este corect să spunem că logica noastră funcționează așa cum era de așteptat.

('ar trebui să apelați selectProduct când ați făcut clic', () => const firstLink = component.find ('a')) first (); firstLink.simulate ('click'); .toEqual (1);)

Enzima vă permite să simulați ușor acțiunile utilizatorilor, cum ar fi clicurile folosind simula() metodă. handlerProductClick.mock.calls.length returnează de câte ori a fost apelată funcția falsă. Ne așteptăm ca acesta să fie egal cu 1.

Celălalt test este relativ ușor. Puteți utiliza funcția găsi() metoda de recuperare a tuturor noduri din DOM. Numarul nodurile ar trebui să fie egale cu lungimea matricei de produse pe care am creat-o mai devreme. 

 aceasta ar trebui să afișeze toate elementele de produs, () => let links = component.find ('a'); așteptați (links.length) .toEqual (productData.length);) 

Testarea stării componentei, a gamei LifeCycleHook și a metodei

În continuare, o să încercăm ProductContainer componentă. Are o stare, un cârlig de viață și o metodă de clasă. Iată afirmațiile care trebuie verificate:

  1. componentDidMount este numit exact o dată.
  2. Starea componentei este populată după montarea componentei.
  3. handleProductClick () metoda trebuie să actualizeze starea când un id de produs este transmis ca argument.

Pentru a verifica dacă componentDidMount a fost chemat, o să spionăm. Spre deosebire de un stub, un spion este folosit când trebuie să testați o funcție existentă. Odată ce spionul este setat, puteți scrie afirmații pentru a confirma dacă funcția a fost apelată.

Puteți spiona o funcție după cum urmează:

src / componente / __ __ Teste / ProductContainer.test.js

 ("ar trebui să sune componentaDidMount o dată", () => componentDidMountSpy = spyOn (ProductContainer.prototype, 'componentDidMount'); // să fie terminată);

Primul parametru la jest.spyOn este un obiect care defineste prototipul clasei pe care o spionam. Al doilea este numele metodei pe care vrem să o spionăm. 

Acum redați componenta și creați o afirmație pentru a verifica dacă a fost apelat spion.

 component = superficial (); aștepta (componentDidMountSpy) .toHaveBeenCalledTimes (1);

Pentru a verifica dacă starea componentei este populată după montarea componentei, putem folosi Enzime stat() pentru a recupera totul în stare. 

("ar trebui să populeze statul", () => component = superficial (); așteptați (component.state (). productList.length) .toEqual (4))

Al treilea este puțin complicat. Trebuie să verificăm asta handleProductClick funcționează conform așteptărilor. Dacă vă îndreptați spre cod, veți vedea că handleProductClick () metoda ia un id de produs ca intrare și apoi actualizează this.state.selectedProduct cu detaliile produsului respectiv. 

Pentru a testa acest lucru, trebuie să invocăm metoda componentei și puteți face asta prin apelare component.instance (). handleProductClick (). Vom introduce un eșantion de produs. În exemplul de mai jos, folosim ID-ul primului produs. Apoi, putem testa dacă statul a fost actualizat pentru a confirma că afirmația este adevărată. Iată întregul cod:

 ("ar trebui să aibă o metodă de lucru numită handleProductClick", () => let firstProduct = productData [0] .id; component = superficial (); . Component.instance () handleProductClick (firstProduct); așteptați (component.state (). selectedProduct) .toEqual (productData [0]); ) 

Am scris 10 teste și dacă totul merge bine, trebuie să vedeți acest lucru:

rezumat

Pfiu! Am acoperit aproape tot ceea ce trebuie să știți pentru a începe să scrieți teste în React folosind Jest și Enzyme. Acum ar putea fi un moment bun să vă îndreptați către site-ul Enzyme pentru a avea o privire mai aprofundată asupra API-ului lor.

Care sunt gândurile tale despre scrierea testelor în React? Mi-ar plăcea să le aud în comentariile.