Kaikkien koodikappaleiden, joilla ei ole testejä, sanotaan olevan vanhoja koodeja Michael Feathersin mukaan. Siksi yksi parhaista tavoista välttää vanhan koodin luominen on käyttää testipohjaista kehitystä (TDD).
Vaikka JavaScriptille ja React.js Yksikkötestaus, tässä viestissä käytämme Jestiä ja Entsyymiä luodaksemme React.js-komponentin, jolla on perustoiminnot TDD: n avulla.
TDD tuo monia etuja koodillesi - yksi korkean testikattavuuden eduista on, että se mahdollistaa koodin helpon korjaamisen samalla, kun pidät koodisi puhtaana ja toimivana.
Jos olet aiemmin luonut React.js-komponentin, olet huomannut, että koodi voi kasvaa todella nopeasti. Se täyttää paljon monimutkaisia olosuhteita, jotka johtuvat tilamuutoksiin ja palvelupuheluihin liittyvistä lausunnoista.
Jokaisella komponentilla, josta yksikötestit puuttuvat, on vanha koodi, jota on vaikea ylläpitää. Voisimme lisätä yksikkötestit tuotantokoodin luomisen jälkeen. Saatamme kuitenkin olla vaarassa jättää huomiotta joitain skenaarioita, jotka olisi pitänyt testata. Luomalla ensin testit meillä on suuremmat mahdollisuudet kattaa kaikki komponenttimme logiikan skenaariot, mikä tekisi helpoksi korjata ja ylläpitää.
React.js-komponentin testaamiseen voidaan käyttää monia strategioita:
props
soitettiin, kun tietty tapahtuma lähetettiin.render
nykyisen komponentin tilan ja sovittaa sen ennalta määritettyyn asetteluun.Näiden strategioiden käyttämiseksi aiomme käyttää kahta työkalua, jotka ovat käteviä työskentelemään React.js-testien kanssa: On ja Entsyymi .
Jest on avoimen lähdekoodin testikehys, jonka on luonut Facebook ja joka on hyvin integroitu React.js: n kanssa. Se sisältää komentorivityökalun testin suorittamiseen, samanlainen kuin Jasmine ja Mocha. Sen avulla voimme myös luoda mallitoimintoja lähes nollakokoonpanolla ja tarjoaa todella mukavan joukon ottelijoita, jotka tekevät väitteistä helpommin luettavia.
Lisäksi se tarjoaa todella mukavan ominaisuuden, nimeltään 'tilannekuvan testaus', joka auttaa meitä tarkistamaan ja tarkistamaan komponentin renderointituloksen. Käytämme tilannevedostestausta komponentin puun sieppaamiseen ja tallentamiseen tiedostoon, jonka avulla voimme verrata sitä renderöintipuuhun (tai mihin tahansa, minkä välitämme expect
-funktiolle ensimmäisenä argumenttina).
Entsyymi tarjoaa mekanismin React.js-komponenttipuiden kiinnittämiseen ja kulkemiseen. Tämä auttaa meitä saamaan pääsyn omiin kiinteistöihinsä ja tilaansa sekä lasten rekvisiittaan väitteidemme suorittamiseksi.
Entsyymi tarjoaa kaksi perustoimintoa komponenttien asennukseen: shallow
ja mount
. shallow
funktio lataa muistiin vain juurikomponentin, kun taas mount
lataa koko DOM-puun.
Yhdistämme Entsyymin ja Jestin yhdistämään React.js-komponentin ja suorittamaan väitteitä sen yli.
Voit katsoa tämä repo , jolla on perusasetukset tämän esimerkin suorittamiseksi.
Käytämme seuraavia versioita:
{ 'react': '16.0.0', 'enzyme': '^2.9.1', 'jest': '^21.2.1', 'jest-cli': '^21.2.1', 'babel-jest': '^21.2.0' }
Ensimmäinen vaihe on luoda epäonnistunut testi, joka yrittää tehdä React.js-komponentin käyttämällä entsyymin matalaa toimintoa.
// MyComponent.test.js import React from 'react'; import { shallow } from 'enzyme'; import MyComponent from './MyComponent'; describe('MyComponent', () => { it('should render my component', () => { const wrapper = shallow(); }); });
Testin suorittamisen jälkeen saamme seuraavan virheen:
ReferenceError: MyComponent is not defined.
Sitten luomme komponentin, joka tarjoaa perussyntaksin testipassin suorittamiseksi.
// MyComponent.js import React from 'react'; export default class MyComponent extends React.Component { render() { return ; } }
Seuraavassa vaiheessa varmistamme, että komponentti hahmottaa ennalta määritetyn käyttöliittymän ulkoasun käyttämällä toMatchSnapshot
Jest-toiminto.
Tämän menetelmän kutsumisen jälkeen Jest luo automaattisesti tilannekuvatiedoston nimeltä [testFileName].snap
, johon lisätään __snapshots__
kansio.
Tämä tiedosto edustaa käyttöliittymän asettelua, jota odotamme komponenttirenderöinniltämme.
Ottaen kuitenkin huomioon, että yritämme tehdä puhdas TDD, meidän pitäisi ensin luoda tämä tiedosto ja soittaa sitten toMatchSnapshot
toiminto testin epäonnistumiseen.
Tämä saattaa kuulostaa hieman hämmentävältä, koska emme tiedä, mitä muotoa Jest käyttää edustamaan tätä asettelua.
Saatat olla kiusaus suorittaa toMatchSnapshot
toiminto ensin ja nähdä tulos tilannekuvatiedostossa, ja se on kelvollinen vaihtoehto. Kuitenkin, jos haluamme todella käyttää puhdas TDD, meidän on opittava, miten tilannekuvatiedostot on rakennettu.
Snapshot-tiedosto sisältää asettelun, joka vastaa testin nimeä. Tämä tarkoittaa, että jos testissämme on tämä muoto:
desc('ComponentA' () => { it('should do something', () => { … } });
Meidän on määritettävä tämä vienti-osiossa: Component A should do something 1
.
Voit lukea lisää tilannekuvien testauksesta tässä .
Joten luomme ensin MyComponent.test.js.snap
tiedosto.
//__snapshots__/MyComponent.test.js.snap exports[`MyComponent should render initial layout 1`] = ` Array [ , ] `;
Sitten luomme yksikkötestin, joka tarkistaa, että tilannekuva vastaa komponentin alielementtejä.
// MyComponent.test.js ... it('should render initial layout', () => { // when const component = shallow(); // then expect(component.getElements()).toMatchSnapshot(); }); ...
Voimme harkita components.getElements
renderöintimenetelmän tuloksena.
Välitämme nämä elementit expect
-menetelmää, jotta tarkistus voidaan suorittaa tilannekuvatiedostoa vastaan.
Testin suorittamisen jälkeen saamme seuraavan virheen:
Received value does not match stored snapshot 1. Expected: - Array [ , ] Actual: + Array []
Jest kertoo meille, että component.getElements
-tulos ei vastaa tilannekuvaa. Joten teemme tämän testipassin lisäämällä syöttöelementin kohtaan MyComponent
.
// MyComponent.js import React from 'react'; export default class MyComponent extends React.Component { render() { return ; } }
Seuraava askel on lisätä toimintoja input
suorittamalla funktio, kun sen arvo muuttuu. Teemme tämän määrittämällä funktion onChange
potkuri
Meidän on ensin muutettava tilannekuva, jotta testi epäonnistuu.
//__snapshots__/MyComponent.test.js.snap exports[`MyComponent should render initial layout 1`] = ` Array [ , ] `;
Tilannekuvan muokkaamisen haittapuoli on se, että rekvisiittausten (tai attribuuttien) järjestys on tärkeä.
Jest lajittelee aakkosjärjestyksessä expect
-sivulle vastaanotetut rekvisiitat toiminto ennen sen tarkistamista tilannekuvan suhteen. Joten meidän tulisi määritellä ne siinä järjestyksessä.
Testin suorittamisen jälkeen saamme seuraavan virheen:
Received value does not match stored snapshot 1. Expected: - Array [ onChange={[Function]} , ] Actual: + Array [ , ]
Tämän testipassin suorittamiseksi voimme yksinkertaisesti antaa tyhjän toiminnon onChange
// MyComponent.js import React from 'react'; export default class MyComponent extends React.Component { render() { return {}} type='text' /> ; } }
Sitten varmistamme, että komponentin tila muuttuu onChange
: n jälkeen tapahtuma lähetetään.
Tätä varten luomme uuden yksikötestin, joka kutsuu onChange
toiminto syötteessä ohittamalla tapahtuma jäljittelemään todellista tapahtumaa käyttöliittymässä.
Sitten tarkistamme, että komponentti osavaltio sisältää avaimen nimeltä input
.
// MyComponent.test.js ... it('should create an entry in component state', () => { // given const component = shallow(); const form = component.find('input'); // when form.props().onChange({target: { name: 'myName', value: 'myValue' }}); // then expect(component.state('input')).toBeDefined(); });
Saamme nyt seuraavan virheen.
Expected value to be defined, instead received undefined
Tämä tarkoittaa, että komponentilla ei ole ominaisuutta tilassa input
.
Teemme testilipun asettamalla tämän merkinnän komponentin tilaan.
// MyComponent.js import React from 'react'; export default class MyComponent extends React.Component { render() { return {this.setState({input: ''})}} type='text' /> ; } }
Sitten meidän on varmistettava, että arvo asetetaan uudessa osavaltiossa. Saamme tämän arvon tapahtumasta.
Tehdään siis testi, joka varmistaa, että tila sisältää tämän arvon.
// MyComponent.test.js ... it('should create an entry in component state with the event value', () => { // given const component = shallow(); const form = component.find('input'); // when form.props().onChange({target: { name: 'myName', value: 'myValue' }}); // then expect(component.state('input')).toEqual('myValue'); }); ~~~ Not surprisingly, we get the following error. ~~ Expected value to equal: 'myValue' Received: ''
Viimeinkin suoritamme tämän testipassin saamalla arvon tapahtumasta ja asettamalla sen tuloarvoksi.
// MyComponent.js import React from 'react'; export default class MyComponent extends React.Component { render() { return { this.setState({input: event.target.value})}} type='text' /> ; } }
Kun olemme varmistaneet, että kaikki testit läpäisevät, voimme muuttaa koodiamme.
Voimme purkaa onChange
: ssa välitetyn funktion ehdottaa uudelle toiminnolle nimeltä updateState
.
// MyComponent.js import React from 'react'; export default class MyComponent extends React.Component { updateState(event) { this.setState({ input: event.target.value }); } render() { return ; } }
Meillä on nyt yksinkertainen React.js-komponentti, joka on luotu TDD: n avulla.
Tässä esimerkissä yritimme käyttää puhdas TDD seuraamalla jokaista vaihetta kirjoittamalla mahdollisimman pieni koodi epäonnistumiseen ja testien läpäisemiseen.
Jotkut vaiheista saattavat tuntua tarpeettomilta, ja meillä voi olla houkutus ohittaa ne. Aina, kun ohitamme minkä tahansa vaiheen, käytämme kuitenkin a vähemmän puhdas TDD-versio.
Vähemmän tiukan TDD-prosessin käyttö on myös kelvollinen ja voi toimia hyvin.
Suosittelen sinulle, että vältät vaiheiden ohittamisen, älä tunne pahaa, jos se on vaikeaa. TDD on tekniikka, jota ei ole helppo hallita, mutta se on ehdottomasti tekemisen arvoinen.
Jos haluat oppia lisää TDD: stä ja siihen liittyvästä käyttäytymislähtöisestä kehityksestä (BDD), lue Pomo ei arvosta TDD: tä kirjoittanut ApeeScapeer Ryan Wilcox.
Testiohjattu kehitys on ohjelmistokehitysprosessi, joka perustuu testi- ja tuotantokoodin kirjoittamisjärjestykseen. Lyhyesti sanottuna haluamme pitää minimissä sekä epäonnistumiseen tarvittavan testikoodin että läpäisemisen edellyttävän tuotantokoodin.
React-komponentti yhdistää logiikan ja esityskoodin. Sitä käytetään pääasiassa tarjoamaan abstrakti kerros verkko- ja mobiilikäyttöliittymäkomponenteille tilan ja ominaisuuksien muuttuessa