QUnit, utviklet av jQuery-teamet, er et flott rammeverk for enhetstesting av JavaScript. I denne opplæringen vil jeg introdusere hva QUnit er spesielt, og hvorfor du bør bry deg om å teste koden din grundig.
QUnit er et kraftig JavaScript-testramme som hjelper deg med å feilsøke kode. Den er skrevet av medlemmer av jQuery-teamet, og er den offisielle testpakken for jQuery. Men QUnit er generell nok til å teste noen vanlig JavaScript-kode, og det kan til og med teste server-side JavaScript via noen JavaScript-motor som Rhino eller V8.
Hvis du ikke er kjent med ideen om "enhetstesting", ikke bekymre deg. Det er ikke så vanskelig å forstå:
I dataprogrammering er enhetstesting en verifikasjons- og valideringsmetode for programvare der en programmerer tester hvis individuelle enheter av kildekoden er egnet til bruk. En enhet er den minste testbare delen av et program. Ved prosessorprogrammering kan en enhet være en individuell funksjon eller prosedyre.
Dette er sitert fra Wikipedia. Enkelt sagt, du skriver tester for hver funksjonalitet av koden din, og hvis alle disse testene er bestått, kan du være sikker på at koden vil være feilfri (det meste avhenger av hvor grundig testene dine er).
Hvis du ikke har skrevet noen enhetstester før, bruker du sannsynligvis bare koden din til et nettsted direkte, klikk et øyeblikk for å se om det oppstår et problem, og prøv å fikse det som du ser på en. Det er mange problemer med denne metoden.
Først er det veldig kjedelig. Å klikke på faktisk er ikke en enkel jobb, fordi du må sørge for at alt er klikket, og det er sannsynlig at du kommer til å savne en ting eller to. For det andre, alt du gjorde for testing, er ikke gjenbrukbart, noe som betyr at det ikke er lett å finne regressjoner. Hva er en regresjon? Tenk deg at du skrev noen kode og testet det, fikset alle feilene du fant, og publiserte det. Deretter sender en bruker noen tilbakemelding om nye feil, og ber om noen nye funksjoner. Du går tilbake til koden, fikser disse nye feilene og legger til disse nye funksjonene. Hva som kan skje neste er at noen av de gamle feilene kommer opp igjen, som kalles "regressions". Se, nå må du klikke igjen, og sjansen er at du ikke finner disse gamle feilene igjen; selv om du gjør det, vil det ta litt tid før du finner ut at problemet skyldes regresjoner. Med enhetstesting skriver du tester for å finne feil, og når koden er endret, filtrerer du den gjennom testene igjen. Hvis det oppstår en regresjon, vil noen tester definitivt bli mislyktes, og du kan enkelt se dem, og vite hvilken del av koden som inneholder feilen. Siden du vet hva du nettopp har endret, kan det enkelt løses.
En annen fordel ved testing av enheter er spesielt for webutvikling: det letter testen av kompatibilitet med nettleseren. Bare kjør testene dine på forskjellige nettlesere, og hvis det oppstår et problem i en nettleser, reparer du det og kjører disse testene igjen, og sørg for at det ikke innfører regresjon på andre nettlesere. Du kan være sikker på at alle målleserne støttes, når de alle bestått testene.
Jeg vil gjerne nevne en av John Resigs prosjekter: TestSwarm. Det krever JavaScript-enhetstesting til et nytt nivå, ved å gjøre det distribuert. Det er et nettsted som inneholder mange tester, alle kan gå dit, kjøre noen av testene, og deretter returnere resultatet tilbake til serveren. På denne måten kan koden bli testet på forskjellige nettlesere og til og med forskjellige plattformer veldig raskt.
Så hvordan skriver du enhetstester med QUnit nøyaktig? Først må du sette opp et testmiljø:
QUnit Test Suite QUnit Test Suite
Som du ser, brukes en vert versjon av QUnit-rammeverket her.
Koden som skal testes, skal settes inn i myProject.js, og testene dine skal settes inn i myTests.js. For å kjøre disse testene, åpner du bare denne HTML-filen i en nettleser. Nå er det på tide å skrive noen tester.
Byggeklossene i enhetstester er påstander.
En påstand er en uttalelse som forutsetter returresultatet av koden din. Hvis prediksjonen er feil, har påstanden mislyktes, og du vet at noe har gått galt.
For å drive påstander, bør du sette dem inn i et testfall:
// La oss teste denne funksjonen erEven (val) returval% 2 === 0; test ('erEven ()', funksjon () ok (erEven (0), 'null er et jevnt tall'); ok (erEven (2), 'så er to'); ok , 'Så er negativ fire'); ok (! Er Even (1), 'En er ikke et jevnt tall'); ok (! ErEven (-7), 'Ingen er negativ syv');)
Her definerte vi en funksjon, erEven, som oppdager om et tall er jevnt, og vi vil teste denne funksjonen for å sikre at den ikke returnerer feil svar.
Vi først ringe test (), som bygger en test sak; Den første parameteren er en streng som vil bli vist i resultatet, og den andre parameteren er en tilbakeringingsfunksjon som inneholder våre påstander. Denne tilbakeringingsfunksjonen vil bli kalt når QUnit er kjørt.
Vi skrev fem påstander, som alle er boolske. En boolsk påstand forventer at den første parameteren skal være sann. Den andre parameteren er også en melding som vil bli vist i resultatet.
Her er hva du får når du har testet:
Siden alle disse påstandene har bestått, kan vi være ganske sikre på at ISEVEN () vil fungere som forventet.
La oss se hva som skjer hvis en påstand har feilet.
// La oss teste denne funksjonen erEven (val) returval% 2 === 0; test ('erEven ()', funksjon () ok (erEven (0), 'null er et jevnt tall'); ok (erEven (2), 'så er to'); ok , 'Så er negativ fire'), ok (! Er Even (1), 'En er ikke et jevnt tall'); ok (! ErEven (-7), 'Ingen negativ syv'); / / Mislykkes ok (3), 'Tre er et jevnt tall');)
Her er resultatet:
Påstanden har mislyktes fordi vi bevisst skrev det feil, men i ditt eget prosjekt, hvis testen ikke passerer, og all påstand er korrekt, vet du at det er funnet en feil.
ok () er ikke den eneste påstanden som QUnit gir. Det finnes andre typer påstander som er nyttige når du tester prosjektet ditt:
Sammenligningserklæringen, er lik (), forventer at den første parameteren (som er den faktiske verdien) er lik den andre parameteren (som er den forventede verdien). Det ligner på ok (), men gir både faktiske og forventede verdier, noe som gjør debugging mye enklere. Liker ok (), det tar en valgfri tredje parameter som en melding som skal vises.
Så i stedet for:
test ('påstander', funksjon () ok (1 == 1, 'en er lik en');)
Du bør skrive:
test ('påstand', funksjon () er lik (1, 1, 'en er lik en');)
Legg merke til den siste "1", som er sammenligningsverdien.
Og hvis verdiene ikke er like:
test ('påstand', funksjon () er lik (2, 1, 'en er lik en');)
Det gir mye mer informasjon, noe som gjør livet enklere.
Sammenligningserklæringen bruker "==" for å sammenligne parametrene, slik at den ikke håndterer array eller objekt sammenligning:
test ('test', funksjon () likes (, , 'mislykkes, disse er forskjellige objekter'), likestiller (a: 1, a: 1, 'mislykkes'); ], [], "mislykkes, det er forskjellige arrays"), likestiller ([1], [1], "mislykkes");)
For å teste denne typen likestilling gir QUnit en annen form for påstand: identisk påstand.
Identisk påstand, samme (), forventer de samme parametrene som likeverdige (), men det er en dyp rekursiv sammenligningserklæring som ikke bare fungerer på primitive typer, men også arrays og objekter. Påstandene, i det forrige eksempelet, vil alle passere hvis du endrer dem til identiske påstander:
test ('test', funksjon () same (, , 'passerer, objekter har samme innhold'); samme (a: 1, a: 1, 'passerer'); [], [], "passerer, arrays har det samme innholdet"); samme ([1], [1], "passerer");)
Legg merke til at det samme () bruker '===' for å sammenligne når det er mulig, så det vil være nyttig når du sammenligner spesielle verdier:
test ('test', funksjon () er lik (0, false, 'true'); samme (0, false, 'false'); falsk'); )
Å sette alle påstander i en enkelt test sak er en veldig dårlig idé, fordi det er veldig vanskelig å vedlikeholde, og gir ikke et rent resultat. Det du bør gjøre er å strukturere dem, sett dem inn i forskjellige testtilfeller, hver med sikte på en enkelt funksjonalitet.
Du kan til og med organisere testtilfeller i forskjellige moduler ved å ringe modulfunksjonen:
modul ('modul A'); test ('en test', funksjon () ); test ('en annen test', funksjon () ); modul ('modul B'); test ('en test', funksjon () ); test ('en annen test', funksjon () );
I tidligere eksempler kalles alle påstandene synkront, noe som betyr at de løper etter hverandre. I den virkelige verden er det også mange asynkronfunksjoner, for eksempel ajax-anrop eller -funksjoner som kalles setTimeout () og setInterval (). Hvordan kan vi teste disse funksjonene? QUnit gir en spesiell type testcase som heter "asynkron test", som er dedikert til asynkron testing:
La oss først prøve å skrive det på en vanlig måte:
test ('asynkron test', funksjon () setTimeout (funksjon () ok (sann);, 100))
Se? Det er som om vi ikke skrev noen påstand. Dette skyldes at påstanden gikk asynkront, da det ble kalt, ble testsaken allerede ferdig.
Her er den riktige versjonen:
test ('asynkron test', funksjon () // Pause testen første stopp (); setTimeout (funksjon () ok (sann); // Etter påstanden er blitt kalt, // fortsett teststart (); , 100))
Her bruker vi stopp () for å stoppe testet, og etter at påstanden er blitt kalt, bruker vi start () for å fortsette.
Anropsstopp () umiddelbart etter anropstest () er ganske vanlig; så QUnit gir en snarvei: asyncTest (). Du kan omskrive det forrige eksempelet slik:
asyncTest ('asynkron test', funksjon () // Testen blir automatisk stoppet settTimeout (funksjon () ok (sann); // Etter at påstanden er blitt kalt, // fortsett teststart ();, 100 ))
Det er en ting å passe på: setTimeout () vil alltid ringe sin tilbakeringingsfunksjon, men hva om det er en tilpasset funksjon (for eksempel et ajax-anrop). Hvordan kan du være sikker på at tilbakeringingsfunksjonen vil bli kalt? Og hvis tilbakeringingen ikke blir kalt, vil start () ikke bli kalt, og hele enhetstesten vil henge:
Så her er det du gjør:
// En tilpasset funksjon funksjon ajax (successCallback) $ .ajax (url: 'server.php', suksess: successCallback); test ('asynkron test', funksjon () // Pause testen, og mislykkes hvis start () ikke kalles etter ett sekund stopp (1000); ajax (funksjon () // ... asynkrone påstander starter );))
Du sender en timeout for å stoppe (), som forteller QUnit, "hvis start () ikke kalles etter tidsavbrudd, bør du mislykkes denne testen." Du kan være sikker på at hele testingen ikke vil henge, og du vil bli varslet om noe går galt.
Hva med flere asynkrone funksjoner? Hvor legger du starten ()? Du setter det i setTimeout ():
// En tilpasset funksjon funksjon ajax (successCallback) $ .ajax (url: 'server.php', suksess: successCallback); test ('asynkron test', funksjon () // Pause teststopp (); ajax (funksjon () // ... asynkrone påstander) ajax (funksjon () // ... asynkrone påstander) setTimeout () start ();, 2000);)
Tidsavbrudd skal være rimelig lenge nok til at begge tilbakekallingene kan ringes før testen fortsetter. Men hva om en tilbakeringing ikke kalles? Hvordan kan du vite det? Dette er hvor forventer () kommer inn:
// En tilpasset funksjon funksjon ajax (successCallback) $ .ajax (url: 'server.php', suksess: successCallback); test ('asynkron test', funksjon () // Pause teststopp (); // Fortell QUnit at du forventer tre påstandene å kjøre forventer (3); ajax (funksjon () ok ajax (funksjon () ok (sant); ok (sant);) setTimeout (funksjon () start ();, 2000);)
Du sender et nummer til å forvente () å fortelle QUnit at du forventer at X mange påstander skal løpe, hvis en av påstanden ikke blir kalt, vil nummeret ikke matche, og du vil bli varslet om at noe gikk galt.
Det er også en snarvei for forventning (): Du sender bare nummeret som den andre parameteren for å teste () eller asyncTest ():
// En tilpasset funksjon funksjon ajax (successCallback) $ .ajax (url: 'server.php', suksess: successCallback); // Fortell QUnit at du forventer tre påstand om å kjøre test ('asynkron test', 3, funksjon () // Pause teststopp (); ajax (funksjon () ok (sann);) ajax () ok (sant); ok (sant);) setTimeout (funksjon () start ();, 2000);)
Det er alt du trenger å vite for å komme i gang witih QUnit. Enhetstesting er en fin metode for å teste koden din før du publiserer den. Hvis du ikke har skrevet noen enhetstester før, er det på tide å komme i gang! Takk for at du leste!