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.
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:
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.
Her er testene som vi skrev i den siste opplæringen:
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.
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.
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 ()))
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 uthevetEnhetstesten vil prøve å hevde følgende forutsetninger:
Her er testen av bare-bein av testen. Den første beforeEach ()
lagrer produktdataene i en variabel, og den andre monterer komponenten.
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.
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.
skal påkalle tilbakeringingsfunksjonen.La oss lage en beforeEach ()
funksjon som fyller ut mock produktdata for våre tester.
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.
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);)
Deretter skal vi teste ProductContainer
komponent. Den har en stat, en livscykelkrok og en klassemetode. Her er påstandene som må bekreftes:
componentDidMount
kalles akkurat en gang.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:
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:
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.