Testing av komponenter i reaksjon ved bruk av jest og enzym

Dette er den andre delen av serien på Testing Components in React. Hvis du har tidligere erfaring med Jest, kan du hoppe videre og bruke GitHub-koden som utgangspunkt. 

I den forrige artikkelen dekket vi de grunnleggende prinsippene og ideene bak testdrevet utvikling. Vi etablerer også miljøet og verktøyene som kreves for å kjøre tester i React. Verktøyet inkluderte Jest, ReactTestUtils, Enzyme og React-Test-renderer. 

Vi skrev deretter et par tester for en demo-applikasjon ved hjelp av ReactTestUtils og oppdaget manglene i forhold til et mer robust bibliotek som Enzyme.

I dette innlegget får vi en dypere forståelse av testkomponenter i React ved å skrive mer praktiske og realistiske tester. Du kan gå til GitHub og klone min repo før du begynner.

Komme i gang med enzymet API

Enzyme.js er et åpen kildekode-bibliotek som vedlikeholdes av Airbnb, og det er en flott ressurs for React-utviklere. Den bruker ReactTestUtils API under, men i motsetning til ReactTestUtils, tilbyr Enzyme en API på høyt nivå og lettforståelig syntaks. Installer Enzyme hvis du ikke allerede har det.

Enzyme-API-en eksporterer tre typer gjengivelsesalternativer:

  1. grunt rendering
  2. full DOM-rendering
  3. statisk gjengivelse

Grunt gjengivelse brukes til å gjengi en bestemt komponent i isolasjon. Barnekomponentene blir ikke gjengitt, og dermed vil du ikke kunne hevde deres oppførsel. Hvis du skal fokusere på enhetstester, vil du elske dette. Du kan grunne en komponent som denne:

importere grunne fra "enzym"; importere ProductHeader fra './ProductHeader'; // Mer konkret eksempel nedenfor. const komponent = grunne (); 

Full DOM-rendering genererer en virtuell DOM av komponenten ved hjelp av et bibliotek kalt jsdom. Du kan benytte denne funksjonen ved å erstatte grunt() metode med holder () i eksempelet ovenfor. Den åpenbare fordelen er at du kan gjøre barnets komponenter også. Hvis du vil teste oppførselen til en komponent med sine barn, bør du bruke dette. 

Statisk gjengivelse brukes til å gjengi reaksjonskomponenter til statisk HTML. Den er implementert ved hjelp av et bibliotek kalt Cheerio, og du kan lese mer om det i dokumentene. 

Revisjon av våre tidligere tester

Her er testene som vi skrev i den siste opplæringen:

src / komponenter / __ tester __ / ProductHeader.test.js

importere ReactTestUtils fra 'reagere-dom / test-utils'; // ES6 beskriver ('ProductHeader Component', () => it ('har en h2 tag', () => const component = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithTag (komponent, 'h2'); ); det ('har en tittelklasse', () => const component = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithClass (komponent, 'tittel'); ))

Den første testen kontrollerer om ProducerHeader komponenten har en

tag, og den andre finner ut om den har en CSS-klasse som heter tittel. Koden er vanskelig å lese og forstå. 

Her er testene omskrevet med Enzyme.

src / komponenter / __ tester __ / ProductHeader.test.js

Import shallow fra "enzym" beskriver ('ProductHeader Component', () => it ('har en h2 tag', () => const komponent = grunne); var node = component.find ('h2'); forvente (node.length) .toEqual (1); ); det ('har en tittelklasse', () => const komponent = grunne (); var node = component.find ('h2'); forvente (node.hasClass ( 'tittel')) toBeTruthy (.); ))

Først opprettet jeg en grunne gjengitt DOM av  komponent bruker grunt() og lagret den i en variabel. Så brukte jeg .finne() metode for å finne en node med tag 'h2'. Det spør etter DOM for å se om det er en kamp. Siden det kun er en forekomst av noden, kan vi sikkert anta det node.length vil være lik 1.

Den andre testen er veldig lik den første. De hasClass ( 'tittel') Metoden returnerer om nåværende node har en klassenavn prop med verdi 'tittel'. Vi kan bekrefte sannheten ved å bruke toBeTruthy ().  

Kjør testene med garntest, og begge testene skal passere. 

Bra gjort! Nå er det på tide å refactor koden. Dette er viktig fra et testerperspektiv fordi lesbare tester er enklere å vedlikeholde. I de ovennevnte testene er de to første linjene identiske for begge testene. Du kan refactor dem ved å bruke en beforeEach () funksjon. Som navnet antyder, den beforeEach funksjon blir kalt en gang før hver spesifikasjon i en beskjed blokk utføres. 

Du kan sende en pilfunksjon til beforeEach () som dette.

src / komponenter / __ tester __ / ProductHeader.test.js

import grunne fra "enzym" beskriver ('ProductHeader Component', () => la komponent, node; // Jest beforeEach () beforeEach (() => komponent = grunne))) beforeEach (() => node = component.find ('h2'))) det ('har en h2 tag', () => expect (node) .toBeTruthy ()); den ('har en tittelklasse', () => forvente (node.hasClass ('title')). toBeTruthy ()))

Skrive Unit Tests med Jest og Enzyme

La oss skrive noen enhetstester for Produkt detaljer komponent. Det er en presentasjonskomponent som viser detaljene til hvert enkelt produkt. 

Vi skal teste delen som er uthevet

Enhetstesten vil prøve å hevde følgende forutsetninger:

  • Komponenten eksisterer og rekvisitter blir lagt ned.
  • Rekvisita som produktets navn, beskrivelse og tilgjengelighet vises.
  • En feilmelding vises når rekvisita er tomme.

Her er testen av bare-bein av testen. Den første beforeEach () lagrer produktdataene i en variabel, og den andre monterer komponenten.

src / komponenter / __ tester __ / ProductDetails.test.js

Beskriv ("ProductDetails component", () => var komponent, produkt; beforeEach (() => product = id: 1, navn: 'Nike Liteforce Blue Sneakers', beskrivelse: 'Lorem ipsum.', status: 'Tilgjengelig';) beforeEach (() => komponent = mount (); ) det ('test # 1', () => ))

Den første testen er enkel:

det ('skulle eksistere', () => forvente (komponent). toBeTruthy (); expect (component.props () .product) .toEqual (product);)

Her bruker vi Rekvisitter() Metode som er nyttig for å få rekvisitter til en komponent.

For den andre testen kan du spørre elementene etter klassenavnene og deretter sjekke om produktets navn, beskrivelse etc. er en del av elementets innertext

 det ('skal vise produktdata når rekvisitter er bestått', () => la tittel = komponent.find ('. produkttittel'); forvent (title.text ()) .Equal (product.name); la description = component.find ('. produktbeskrivelse'); forvente (description.text ()) .Equal (product.description);) 

De tekst() Metoden er spesielt nyttig i dette tilfellet for å hente den indre teksten til et element. Prøv å skrive en forventning til product.status () og se om alle tester passerer.

For den endelige testen skal vi montere Produkt detaljer komponent uten rekvisitter. Deretter skal vi lete etter en klasse med navnet ".product-error" og se om det inneholder teksten "Beklager, produktet eksisterer ikke".

 det ('skal vise en feil når rekvisitter ikke er bestått', () => / * komponent uten rekvisitter * / komponent = mount (); la node = component.find ('. produkt-feil'); forvente (node.text ()). toEqual ('Beklager. Produktet finnes ikke'); ) 

Det er det. Vi har testet komponent i isolasjon. Test av denne typen er kjent som enhetstester.

Teste tilbakeringinger ved hjelp av stubber og spioner

Vi har nettopp lært å teste rekvisitter. Men for å virkelig teste en komponent i isolasjon, må du også teste tilbakeringingsfunksjonene. I denne delen skriver vi tester for Produktliste komponent og opprett stubber for tilbakeringingsfunksjoner underveis. Her er forutsetningene som vi må hevde.

  1. Antallet oppførte produkter skal svare til antall objekter komponenten mottar som rekvisitter.
  2. Klikk på skal påkalle tilbakeringingsfunksjonen.

La oss lage en beforeEach () funksjon som fyller ut mock produktdata for våre tester.

src / komponenter / __ tester __ / ProductList.test.js

 forEach (() => productData = [id: 1, navn: 'NIKE Liteforce Blue Sneakers', beskrivelse: 'Lorem ipsu.', status: 'Tilgjengelig', // Utelatt for korthet])

La oss nå montere vår komponent i en annen beforeEach () blokkere.

beforeEach (() => handleProductClick = jest.fn (); component = mount (  ); )

De Produktliste mottar produktdata gjennom rekvisitter. I tillegg til det mottar det tilbakering fra foreldrene. Selv om du kan skrive tester for foreldrenes tilbakeringingsfunksjon, er det ikke en god ide om målet ditt er å holde seg til enhetsprøver. Siden tilbakeringingsfunksjonen tilhører den overordnede komponenten, vil innlemmelsen av foreldrenes logikk gjøre tester kompliserte. I stedet skal vi lage en stubfunksjon.

Hva er en Stub? 

En stub er en dummy-funksjon som later til å være en annen funksjon. Dette gjør at du kan teste en komponent uavhengig uten å importere foreldre eller barnekomponenter. I eksemplet ovenfor opprettet vi en stubfunksjon kalt handleProductClick ved å påkalle jest.fn ()

Nå trenger vi bare å finne alle elementer i DOM og simulere et klikk på den første node. Etter å ha blitt klikket, vil vi sjekke om handleProductClick () ble påkalt. Hvis ja, det er rimelig å si at vår logikk fungerer som forventet.

det ('skal ringe selectProduct når du klikker', () => const firstLink = component.find ('a'). første (); firstLink.imitere ('klikk'); forvente (handleProductClick.mock.calls.length) .tilEkvivalent (1);))

Enzym lar deg enkelt simulere brukerhandlinger som klikk ved hjelp av simulere () metode. handlerProductClick.mock.calls.length returnerer antall ganger mock-funksjonen ble kalt. Vi forventer at den skal være lik 1.

Den andre testen er relativt enkel. Du kan bruke finne() metode for å hente alt noder i DOM. Antallet av noder skal være lik lengden på produktdata-arrayet som vi opprettet tidligere. 

 det ('skal vise alle produktelementer', () => let links = component.find ('a'); forvente (links.length) .toEqual (productData.length);) 

Testing av komponentens tilstand, LifeCycleHook og Method

Deretter skal vi teste ProductContainer komponent. Den har en stat, en livscykelkrok og en klassemetode. Her er påstandene som må bekreftes:

  1. componentDidMount kalles akkurat en gang.
  2. Komponentets tilstand er befolket etter komponentfestene.
  3. De handleProductClick () Metode skal oppdatere staten når et produkt-id sendes inn som et argument.

For å sjekke om componentDidMount ble kalt, vi skal spionere på den. I motsetning til en stub brukes en spion når du må teste en eksisterende funksjon. Når spion er satt, kan du skrive påstander om å bekrefte om funksjonen ble kalt.

Du kan spionere på en funksjon som følger:

src / komponenter / __ tester __ / ProductContainer.test.js

 det ('skal ringe componentDidMount once', () => componentDidMountSpy = spyOn (ProductContainer.prototype, 'componentDidMount'); // For å være ferdig);

Den første parameteren til jest.spyOn er et objekt som definerer prototypen av klassen som vi spionerer på. Den andre er navnet på metoden vi vil spionere. 

Gjør nå komponenten og legg inn en påstand for å sjekke om spion ble kalt.

 komponent = grunne (); forvente (componentDidMountSpy) .toHaveBeenCalledTimes (1);

For å kontrollere at komponentens tilstand er fylt etter komponentfestene, kan vi bruke Enzyme stat() metode for å hente alt i staten. 

det ('burde fylle staten', () => komponent = grunne (); forvente (component.state (). productList.length) .toEqual (4))

Den tredje er litt vanskelig. Vi må bekrefte det handleProductClick jobber som forventet. Hvis du går over til koden, ser du at handleProductClick () Metoden tar et produkt-ID som input, og deretter oppdateres this.state.selectedProduct med detaljer om det produktet. 

For å teste dette må vi påberope komponentens metode, og du kan faktisk gjøre det ved å ringe component.instance (). handleProductClick (). Vi sender forbi et produkt-produkt-ID. I eksemplet nedenfor bruker vi ID for det første produktet. Deretter kan vi teste om staten ble oppdatert for å bekrefte at påstanden er sant. Her er hele koden:

 det ('burde ha en arbeidsmetode kalt handleProductClick', () => la førstProdukt = produktData [0] .id; komponent = grunt (); . Component.instance () handleProductClick (firstProduct); Forvent (component.state (). selectedProduct) .toEqual (productData [0]); ) 

Vi har skrevet 10 tester, og hvis alt går bra, så er dette det du bør se:

Sammendrag

Puh! Vi har dekket nesten alt du trenger å vite for å komme i gang med å skrive tester i React using Jest and Enzyme. Nå kan det være en god tid å gå over til Enzyme-nettstedet for å få et dypere titt på API-en.

Hva er tankene dine om å skrive tester i React? Jeg vil gjerne høre dem i kommentarene.