Vi vet alle at vi skal teste koden vår, men vi gjør det egentlig ikke. Jeg antar det er rimelig å si at de fleste av oss slår det av fordi, ni ganger ut av ti, betyr det å lære enda et konsept. I denne opplæringen vil jeg introdusere deg til et flott lite rammeverk for å teste JavaScript-koden din med letthet.
Forresten, visste du at du kan få JavaScript-feilene dine løst raskt og enkelt av en ekspert på Envato Studio?
ThemeManiac, for eksempel, vil reparere javaScript-feil eller webkompatibilitetsproblemer på ditt nettsted eller webapplikasjon. Rettene kan fullføres raskt, basert på kompleksiteten og tilgjengelig informasjon. Han kan også omorganisere skriptene dine og få en helt ny brukeropplevelse. Han har gjennomført mer enn 1000 jobber på Envato Studio, med 99% av kundene som anbefalte ham.
I dag skal vi lære om Jasmine BDD testing rammeverket. Men vi stopper her for en omvei først, for å snakke veldig kort, om BDD og TDD. Hvis du ikke er kjent med disse akronymer, står de for Behavior-Driven Development og Testdrevet utvikling. Jeg er midt i å lære om hva hver av disse er i praksis og hvordan de er forskjellige, men her er noen av de grunnleggende forskjellene:
BDD og TDD? står for Behavior-Driven Development og Testdrevet utvikling.
TDD i sin enkleste form er bare dette:
Det er ganske lett å forstå, eh?
BDD er litt mer komplisert: Som jeg forstår det akkurat nå, tror jeg ikke at du eller jeg som en enkelt utvikler faktisk kan øve det fullt ut; det er mer av et lag ting. Her er noen av praksisene i BDD:
For å lære mer, kan du lese den omfattende Wikipedia-artikkelen (hvorfra poengene ble tatt).
Alt dette for å si at mens Jasmine regner seg som et BDD-rammeverk, skal vi bruke det på en mer TDD-stil måte. Det betyr ikke at vi bruker det galt, skjønt. Når vi er ferdige, kan du enkelt teste JavaScript? og jeg forventer at du skal gjøre det!
Jasmine tar mange tegn fra Rspec.
Hvis du er kjent med Rspec, er de facto BDD-rammeverk, ser du at Jasmine tar mange tegn fra Rspec. Jasmintester er hovedsakelig to deler: beskrive
blokker og den
blokker. La oss se hvordan dette virker.
Vi vil se på noen nærmere tester i noen, men for nå vil vi holde det enkelt:
Beskriv ('JavaScript tilleggsoperatør', funksjon () det ('legger til to tall sammen', funksjon () forventer (1 + 2). tilEkvivalent (3);););
Begge beskrive
og den
Funksjoner tar to parametre: en tekststreng og en funksjon. De fleste testrammer prøver å lese så mye som engelsk som mulig, og du kan se dette med Jasmine. Først legg merke til at strengen gikk til beskrive
og strengen gikk til den
danner en setning (av sorter):? JavaScript tilleggsoperatør legger til to tall sammen.? Så fortsetter vi å vise hvordan.
Inne i det den
blokkere, kan du skrive all oppsettskoden du trenger for testen din. Vi trenger ikke noe for dette enkle eksemplet. Når du er klar til å skrive den faktiske testkoden, starter du med forvente
funksjon, passerer det uansett hva du tester. Legg merke til hvordan dette danner en setning også: vi forventer 1 + 2 til lik 3.?
Men jeg kommer foran oss selv. Som jeg sa, hvilken verdi du passerer inn i forvente
vil bli testet. Metoden du ringer, av den returnerte verdien av forvente
, vil bli bestemt av hvilken test som kjøres. Denne gruppen av metoder kalles 'matchers', og vi ser på flere av dem i dag. I dette tilfellet bruker vi toEqual
matcher, som sjekker for å se at verdien har gått til forvente
og verdien gikk til toEqual
er den samme verdien.
Jeg tror du er klar til å ta dette til neste nivå, så la oss sette opp et enkelt prosjekt ved hjelp av Jasmine.
Jasmine kan brukes av seg selv; eller du kan integrere det med et Rails-prosjekt. Vi skal gjøre det forrige. Mens Jasmine kan løpe utenfor nettleseren (tenk Node, blant annet), kan vi få en veldig fin liten mal med nedlastingen.
Så, gå videre til den frittstående nedlastingssiden og få den nyeste versjonen. Du bør få noe slikt:
Du finner de faktiske Jasmine rammefilene i lib
mappe. Hvis du foretrekker å strukturere prosjektene dine annerledes, vennligst gjør det; men vi skal holde dette for nå.
Det er faktisk noen prøvekode koblet opp i denne prosjektmalen. Den faktiske? JavaScript (koden vi vil teste) finnes i src
katalogen; Vi skal snart sette oss der. Testkoden - den specs-gå inn i spec
mappe. Ikke bekymre deg for SpecHelper.js
filen bare ennå; vi kommer tilbake til det.
At SpecRunner.html
fil er det som driver testene i en nettleser. Åpne den opp (og merk av for "passet" -feltet i øverste høyre hjørne), og du bør se noe slikt:
Dette viser oss at alle testene for prøveprosjektet passerer. Når du har kommet gjennom denne opplæringen, anbefaler jeg at du åpner spec / PlayerSpec.js
fil og les den koden. Men akkurat nå, la oss gi denne testen skrive ting en prøve.
convert.js
i src
mappe.convertSpec.js
i spec
mappe,SpecRunner.html
fil og endre navn på den SpecRunner.original.html
.Fjern koblingene til utvalgsprosjektfilene i SpecRunner.html
og legg til disse linjene:
Nå er vi klare til å lage et mini-bibliotek som konverterer mellom måleenheter. Vi starter med å skrive tester for vårt mini-bibliotek.
Så, la oss skrive våre tester, skal vi?
Beskriv ("Konverter bibliotek", funksjon () beskriv ("avstandskonverter", funksjon () ); beskriv ("volumomformer", funksjon () ););
Vi starter med dette; vi tester vår Konvertere
bibliotek. Du vil legge merke til at vi nesting beskrive
uttalelser her. Dette er helt lovlig. Det er faktisk en fin måte å teste separate funktjonsbiter av samme kodebase. I stedet for to separate beskrive
krever Konvertere
bibliotekets avstandskonverteringer og volumkonverteringer, kan vi få en mer beskrivende pakke med tester som dette.
Nå, på de faktiske testene. Jeg gjentar det indre beskrive
ringer her for din bekvemmelighet.
beskrive ("avstandskonverter", funksjon () det "omformer tommer til sentimeter", funksjon () forventer (Konverter (12, "i"). til ("cm")) .Equal (30,48);) ; den ("konverterer sentimeter til meter", funksjon () forventer (Konverter (2000, "cm"). til ("yards")) .Equal (21.87);););
Her er våre tester for avstandskonverteringer. Det er viktig å legge merke til noe her: Vi har ikke skrevet et speck kode for vår Konvertere
bibliotek, men i disse testene gjør vi mer enn bare å se om det virker: Vi bestemmer faktisk hvordan den skal brukes (og derfor implementeres). Slik har vi besluttet å gjøre konverteringene våre:
Konvertere(, ).til( );
Ja, jeg tar en cue fra måten Jasmine har implementert sine tester på, men jeg synes det er et fint format. Så i disse to testene har jeg konvertert meg selv (ok, med en kalkulator) for å se hva resultatene av våre samtaler skal være. Vi bruker toEqual
matcher for å se om våre tester passerer.
Her er volumtestene:
beskrive ("volumomformer", funksjon () det konverterer liter til gallon », funksjon () forventer (Konverter (3," liter ") til (" gallons ")) .Equal (0.79);) ; ("konverterer gallon til kopper", funksjon () forvente (Konverter (2, "gallon"). til ("kopper")) .Equal (32);););
Og jeg skal legge til to tester i toppnivå beskrive
anrop:
det ("kaster en feil ved bestått en ukjent fra-enhet", funksjon () var testFn = funksjon () Konverter (1, "dollar") til ("yens"); forvente (testFn) .toThrow ny feil ("ukjent fra enhet"));); det ("kaster en feil ved bestått en ukjent enhet", funksjon () var testFn = funksjon () Konverter (1, "cm") til ("furlongs"); forvente (testFn) .toThrow ny feil ("ukjent til enhet")););
Disse kontrollene for feil som skal kastes når ukjente enheter sendes inn i enten Konvertere
funksjon eller til
metode. Du vil legge merke til at jeg pakker den faktiske konverteringen i en funksjon og overfører den til forvente
funksjon. Det er fordi vi ikke kan ringe funksjonen som forvente
parameter; Vi må gi den en funksjon og la den kalle selve funksjonen. Siden vi må sende en parameter til det til
funksjon, kan vi gjøre det på denne måten.
Den andre tingen å merke seg er at jeg introduserer en ny matcher: å kaste
, som tar et feilobjekt. Vi ser snart på noen flere kampanjer.
Nå, hvis du åpner SpecRunner.html
i en nettleser får du dette:
Flott! Våre tester er sviktende. Nå, la oss åpne vår convert.js
fil og gjør noe arbeid:
Funksjon Konverter (tall, fraUnit) var konverteringer = avstand: meter: 1, cm: 0,01, føtter: 0,3048, tommer: 0,0254, meter: 0,9144, volum: liter: 1, gallon: 3,785411784, kopper: 0,236588236 , mellomUnit = falsk, type, enhet; for (skriv inn konverteringer) if (konverteringer (type)) if ((unit = konverteringer [type] [fraUnit])) mellomUnit = tall * enhet * 1000; returnere til: funksjon (toUnit) if (betweenUnit) for (skriv inn konverteringer) if (conversions.hasOwnProperty (type)) if ((unit = konverteringer [type] [toUnit])) fikse (mellomUnit / (enhet * 1000)); kaste ny feil ("ukjent til enhet"); ellers kaste ny feil ("ukjent fra-enhet"); funksjonsrett (num) return parseFloat (num.toFixed (2)); ;
Vi kommer ikke til å diskutere dette, fordi vi lærer Jasmine her. Men her er hovedpoengene:
meter: 0,9144
, du vet at det er hvor mange meter det er i en meter. Så, for å konvertere meter til, si, centimeter, vi multipliserer yards
ved den første parameteren (for å få antall meter) og deretter dele produktet med cm
, antall meter i en centimeter. På denne måten behøver vi ikke lagre konverteringsfrekvensene for hvert par verdier. Dette gjør det også enkelt å legge til nye verdier senere.fromUnit
til høyre tast.Konvertere
funksjon, lagrer vi mellomverdien i betweenUnit
, som er initialisert til falsk
. På den måten, hvis vi ikke har fromUnit
, betweenUnit
vil være falsk
går inn i til
metode, og så en feil med kastes.toUnit
, en annen feil vil bli kastet. Ellers deler vi som nødvendig og returnerer den konverterte verdien.Nå, gå tilbake til SpecRunner.html
og last siden på nytt. Du bør nå se dette (etter å ha lest? Vis bestått?):
Der går du! Våre tester går forbi. Hvis vi utviklet et virkelig prosjekt her, ville vi skrive tester for en viss del av funksjonalitet, få dem til å passere, skrive tester for en annen sjekk, få dem til å passere osv. Men siden dette var et enkelt eksempel, har vi nettopp gjort det alt i ett fall.
Og nå som du har sett dette enkle eksempelet på å bruke Jasmine, la oss se på noen få flere funksjoner som den tilbyr deg.
Så langt har vi brukt to matchere: toEqual
og å kaste
. Det er selvsagt mange andre. Her er noen du vil sikkert finne nyttig; Du kan se hele listen på wiki.
Hvis du bare vil sørge for at en variabel eller egenskap er definert, er det en matcher for det. Det er også en som bekrefter at en variabel eller eiendom er udefinert
.
det ("er definert", funksjon () var navnet = "Andrew"; forvente (navn) .toBeDefined ();) det ("er ikke definert", funksjon () var navn; forventer (navn). tilBeUdedefinert (););
Hvis noe skal være sant eller falskt, vil disse kampene gjøre det.
det ("er sant", funksjon () forventer (Lib.isAWeekDay ()). toBeTruthy ();); det ("er falskt", funksjon () forventer (Lib.finishedQuiz) .toBeFalsy (););
For alt du nummererer folk. Du vet hvordan disse fungerer:
det ("er mindre enn 10", funksjon () forventer (5) .tilBeLessThan (10);); det ("er større enn 10", funksjon () forvente (20) .toBeGreaterThan (10););
Har du noe utskriftstekst som skal matche et vanlig uttrykk? De å passe
Matcher er klar og villig.
det ("outputs the right text", funksjon () forvente (cart.total ()). toMatch (/ \ $ \ d *. \ d \ d /););
Denne er ganske nyttig. Det kontrollerer for å se om et array eller en streng inneholder et element eller en substring.
det ("skal inneholde appelsiner", funksjon () forvente (["epler", "appelsiner", "pærer"]) .Contain ("appelsiner"););
Det er også noen andre matchere som du finner i wiki. Men hva om du vil ha en matcher som ikke eksisterer? Virkelig, du bør kunne gjøre omtrent alt med noen oppsettkode og kampanjene Jasmine gir, men noen ganger er det bedre å abstrahere noen av den logikken for å få en lesbar test. Serendipitøst (vel, faktisk ikke), tillater Jasmine oss å skape våre egne kampere. Men for å gjøre dette må vi lære litt noe annet først.
Ofte - når du tester en kodebase - vil du utføre noen få linjer med oppsettkoden for hver test i en serie. Det ville være smertefullt og verbose å måtte kopiere det for alle den
ring, så Jasmine har en praktisk liten funksjon som gjør at vi kan utpeke koden til å kjøre før eller etter hver test. La oss se hvordan dette virker:
beskrive ("MyObject", funksjon () var obj = ny MyObject (); beforeEach (funksjon () obj.setState ("clean");); den ("endrer tilstand", funksjon () obj.setState ("skitne"), forvent (obj.getState ()) .Equal ("dirty");) det ("legger til stater", funksjon () obj.addState ("pakket"); )) .Ekvivalent (["rent", "pakket"]););
I dette utførlige eksemplet kan du se hvordan, før hver test kjøres, tilstanden til obj
er satt til? ren ?. Hvis vi ikke gjorde dette, fortsetter den endrede til et objekt i en tidligere test som standard ved neste test. Selvfølgelig kunne vi også gjøre noe lignende med AfterEach
funksjon:
beskrive ("MyObject", funksjon () var obj = ny MyObject ("clean"); // setter initial tilstand afterEach (funksjon () obj.setState ("clean");); , funksjon () obj.setState ("dirty"); expect (obj.getState ()) .Equal ("dirty");) det ("legger til stater", funksjon () obj.addState ("pakket" ); forvente (obj.getState ()) .Equal (["clean", "packaged"]););
Her setter vi opp objektet til å begynne med, og deretter har det rettet etter hver test. Hvis du vil ha MyObject
fungere slik at du kan gi denne koden et forsøk, du kan få det her i et GitHub-gist.
Som vi sa tidligere, vil kundematchere trolig være nyttig til tider. Så la oss skrive en. Vi kan legge til en matcher i enten a BeforeEach
ring eller en den
ringe (vel, jeg antar deg kunne gjør det i en AfterEach
ring, men det ville ikke gi mye mening). Slik begynner du:
beforeEach (funksjon () this.addMatchers (););
Ganske enkelt, eh? Vi ringer this.addMatchers
, sender den en objektparameter. Hver nøkkel i dette objektet blir navnet på en kampfører, og den tilknyttede funksjonen (verdien) blir hvordan den kjøres. La oss si at vi vil lage en matcher som med sjekk for å se om ett tall er mellom to andre. Her er hva du vil skrive:
beforeEach (funksjon () this.addMatchers (toBeBetween: funksjon (rangeFloor, rangeCeiling) if (rangeFloor> rangeCeiling) var temp = rangeFloor; rangeFloor = rangeCeiling; rangeCeiling = temp; returnere dette.aktuelle> rangeFloor && this. faktiske < rangeCeiling; ); );
Vi tar bare to parametere, sørg for at den første er mindre enn den andre, og returner en boolsk uttalelse som vurderer til sann hvis betingelsene våre er oppfylt. Det viktige å merke seg her er hvordan vi får tak i verdien som ble sendt til forvente
funksjon: this.actual
.
det ("er mellom 5 og 30", funksjon () forvente (10). tilBeBetween (5, 30);); det ("er mellom 30 og 500", funksjon () forvente (100). tilBeBetween (500, 30););
Dette er hva SpecHelper.js
filen gjør; den har en beforeEach
samtale som legger til spilleren tobePlaying ()
. Sjekk det ut!
Det er mye mer du kan gjøre med Jasmine: funksjonsrelaterte matchere, spioner, asynkrone detaljer og mer. Jeg anbefaler at du undersøker wiki hvis du er interessert. Det er også noen medfølgende biblioteker som gjør testing i DOM enklere: Jasmine-jQuery og Jasmine-fixture (som avhenger av Jasmine-jQuery).
Så hvis du ikke tester JavaScript så langt, er det nå en fin tid å starte. Som vi har sett, gjør Jasmines raske og enkle syntaks testen ganske enkel. Det er bare ingen grunn til at du ikke skal gjøre det, nå er det der?
Hvis du vil ta JavaScript-utviklingen ytterligere, hvorfor ikke sjekk utvalget av JavaScript-elementer på Envato Market? Det er tusenvis av skript, apper og kodestykker som hjelper deg.