Ved å sørge for at søknaden din er testet, kan du redusere mengden av feil du finner i koden din, øke vedlikeholdet av søknaden din og utforme godt strukturert kode.
Testing av klientsidenheten presenterer ulike utfordringer enn testen på serversiden. Når du arbeider med klientens sidekode, vil du finne deg selv å streve for å skille applikasjonslogikk fra DOM-logikken, så vel som bare å strukturere JavaScript-koden generelt. Heldigvis finnes det mange flotte klientsidetestbiblioteker der ute for å teste koden din, lage beregninger om testdekning, samt analysere kompleksiteten av det.
Først av alt, er enhetstesting generelt en måte å redusere feil ved å sikre at søknaden fungerer som den skal. Utover det skjønt, er forestillingene om Test Driven Development (TDD) og Behavior Driven Development (BDD).
Disse to enhetens teststrategier hjelper deg med å designe søknaden din ved å skrive tester før du skriver din søknadslogikk. Ved å skrive testene før du skriver koden, gir du deg muligheten til å tenke gjennom utformingen av søknaden din.
Dette skjer fordi når du skriver tester, prøver du i utgangspunktet å utforme APIen for hvordan du samhandler med koden din, slik at du får bedre innsikt i designen. Testing første vil raskt vise deg eventuelle feil du har i ditt design fordi du skriver testkode som i hovedsak bruker koden du skriver!
TDD er en kodeoppdagingsprosess
Du lærer at TDD hjelper deg med å oppdage koden din mens du skriver den. TDD er oppsummert veldig raskt som "Rød, Grønn, Refactor". Hva dette betyr er, du skriver en test, skriv nok kode for å gjøre testen mislykkes først. Deretter, du skriver koden som gjør testen din. Etter det tenker du gjennom det du nettopp skrev og refactor det. Fint og enkelt.
BDD er en litt annen takk på TDD og er basert mer på forretningsbehov og spesifikasjoner.
Det er mange grunner til at du skal teste klientens sidekode. Som nevnt før, vil det bidra til å redusere feil, og hjelpe deg med å designe søknaden din. Klientsiden testing er også viktig fordi det gir deg en sjanse til å teste frontendekoden din, isolert, bort fra presentasjonen din. Med andre ord, en av fordelene er at du får teste JavaScript-koden din uten å spinne opp en applikasjonsserver. Du kjører bare testene og sørger for at ting fungerer uten å klikke rundt og teste ting. I mange tilfeller trenger du ikke engang internettilgang, så lenge testene dine er satt opp riktig.
Med JavaScript som tar en så viktig rolle i moderne webutvikling, er det viktig å lære å teste koden og redusere sjansene for at feilene kommer inn i produksjonskoden. Sjefen din liker det ikke når det skjer, og heller ikke du! Faktisk er et godt sted å komme i gang med å jobbe med klientsiden testing å skrive tester rundt en feilrapport. Dette vil tillate deg å få øve på å skrive tester når du ikke har et sted å starte fra bunnen av.
En annen grunn til å teste klientens sidekode er at når en pakke med tester eksisterer og har anstendig dekning over koden din, når du er klar til å gå og legge til nye funksjoner i koden din, kan du legge til den nye funksjonen, re -kjør testene dine, og kontroller at du ikke angre og bryte noen eksisterende funksjoner.
Komme i gang med klientsiden testing kan være skremmende hvis du aldri har gjort det før. En av de vanskeligste delene om klientsiden testing er å finne ut den beste måten å isolere DOM fra søknadens logikk. Det betyr ofte at du trenger en form for abstraksjon over DOM. Den enkleste måten å oppnå dette på er gjennom en klientsiden rammeverk som Knockout.js, Backbone.js eller Angular.js, for å nevne noen få.
Når du bruker et bibliotek som disse, kommer du til å tenke mindre om hvordan siden din gjøres i nettleseren og mer om funksjonene i søknaden din. Det er ikke som om det er umulig å prøve enhet med bare vanlig JavaScript skjønt. I så fall vil livet ditt bli enklere hvis du designer koden på en slik måte at DOM enkelt kan abstraheres.
Det er mange forskjellige testbiblioteker å velge mellom, selv om de tre frontløpene pleier å være QUnit, Mokka og Jasmine.
Jasmine og mokka er begge fra BDD-skolen av enhetstesting, mens QUnit bare er et enhetstestingsramme av sin egen.
For resten av dette innlegget, vil vi undersøke ved å bruke QUnit som barriere for oppføring i klientsiden testing er svært lav. Sjekk ut denne detaljerte introen til QUnit for mer informasjon.
Komme i gang med QUnit er ekstremt enkelt. Følgende HTML er alt du trenger:
QUnit Eksempel
For de neste eksemplene, la oss anta at vi bygger en liten widget som du skriver inn en postnummer i en tekstboks, og den returnerer tilsvarende by, stat og fylke verdier ved hjelp av Geonames. Det vil bare vise en postnummer først, men så snart postnummeret har fem tegn, henter det data fra Geonames. Hvis det er mulig å finne data, vil det vise noen få flere felt som inneholder den resulterende byen, staten og fylkeskommunen. Vi bruker også Knockout.js. Det første trinnet er å skrive en feiltest.
Tenk gjennom designet litt før den første testen er skrevet, det må sannsynligvis være minst to visningsmodeller, så det vil være et godt utgangspunkt. For å starte, definerer vi en QUnit-modul og vår første test:
modul ("postnummer retriever"); test ("visningsmodeller bør eksistere", funksjon () ok (FormViewModel, "En visningModel for vårt skjema bør eksistere"); ok (AddressViewModel, "En visningModel for adressen vår bør eksistere"););
Hvis du kjører denne testen, vil den mislykkes, nå kan du gå skrive koden for å få det til å passere:
var AddressViewModel = funksjon (alternativer) ; var FormViewModel = funksjon () this.address = new AddressViewModel (); ;
Du ser grønn i stedet for rød denne gangen. Tester som dette virker litt dumme først, men de er nyttige fordi de tvinger deg til å tenke gjennom noen av de tidlige stadiene av designen din.
Den neste testen vi skal skrive vil fungere på AddressViewModel
funksjonalitet. Vi vet fra beskrivelsen av denne widgeten at de andre feltene skal være skjult først, til dataene er funnet for postnummeret.
modul ("adressevisningsmodell"); test ("skal vise bystatsdata hvis en postnummer er funnet", funksjon () var adresse = ny AddressViewModel (); ok (! address.isLocated ()); address.zip (12345); address.city foo "); address.state (" bar "); address.county (" bam "); ok (address.isLocated ()););
Ingen av koden for dette har blitt skrevet ennå, men ideen her er at ligger
vil være en beregningsbar observerbar, som returnerer ekte
bare når zip, by, state og county er alle sannhet. Så, denne testen vil selvfølgelig mislykkes først, nå la oss skrive koden for å få det til å passere.
var AddressViewModel = funksjon (alternativer) options = options || ; this.zip = ko.observable (options.zip); this.city = ko.observable (options.city); this.state = ko.observable (options.state); this.county = ko.observable (options.county); this.isLocated = ko.computed (function () return this.city () && this.state () && this.county () && this.zip ();, dette); this.initialize (); ;
Nå hvis du kjører testene igjen, ser du grønt!
Dette er, på grunnlag av det mest grunnleggende, hvordan du kan bruke TDD til å skrive frontendestester. Ideelt sett vil du etter hver sviktende test skrive den enkleste koden som vil gjøre testen pass og deretter gå tilbake og refactor koden. Du kan lære mye mer om TDD-praksis selv, så jeg foreslår at du leser og studerer det videre, men de forrige eksemplene er nok til å få tankene dine å tenke på å skrive tester først.
Sinon.js er et JavaScript-bibliotek som gir muligheten til å spionere, stubbe og mocke JavaScript-objekter. Når du skriver enhetstester, vil du forsikre deg om at du kun kan teste en gitt "enhet" av kode. Dette betyr ofte at du må gjøre en slags mocking eller stubbing av avhengigheter for å isolere koden som blir testet.
Sinon har en ekstremt enkel API for å gjøre dette. Geonames API støtter å hente data via et JSONP-endepunkt, noe som betyr at vi lett kan bruke $ .ajax
.
Ideelt sett skjønner du imidlertid ikke på Geonames API i testene dine. De kan være midlertidige, Internett kan dø, og det er bare tregere å faktisk gjøre ajax-anropet. Sinon til redning.
test ("skal bare prøve å få data hvis det er 5 tegn", funksjon () var adresse = ny AddressViewModel (); sinon.stub (jQuery, "ajax"). returnerer (ferdig: $ .noop); .zip (1234); ok (! jQuery.ajax.calledOnce); address.zip (12345); ok (jQuery.ajax.calledOnce); jQuery.ajax.restore (););
Her i denne testen gjør vi noen ting. Først av alt, sinon.stub
funksjonen går faktisk til proxy over jQuery.ajax
og legg til evnen til å se hvor mange ganger den har blitt kalt, og mange andre påstander. Som testen lyder, "bør bare prøve å få data hvis det er 5 tegn", vi kommer til å anta at når adressen er satt til bare"1234
", at det ikke er gjort ajax-anrop ennå, og sett det til"12345
", og på det tidspunktet bør det bli foretatt et ajax-anrop.
Vi må da gjenopprette jQuery.ajax
til sin opprinnelige tilstand, fordi vi er gode borgere av enhetstesting og ønsker å holde testene våre atomiske. Å holde testene atomiske er viktig for å sikre at en test ikke er avhengig av en annen test, og det er ingen delt tilstand mellom testene. De kan da kjøres i hvilken som helst rekkefølge.
Nå som testen er skrevet, kan vi kjøre den, se den mislykkes, og skriv koden som utfører ajax-forespørselen til Geonames.
AddressViewModel.prototype.initialize = function () this.zip.subscribe (this.zipChanged, dette); ; AddressViewModel.prototype.zipChanged = funksjon (verdi) if (value.toString (). Lengde === 5) this.fetch (value); ; AddressViewModel.prototype.fetch = funksjon (zip) var baseUrl = "http://www.geonames.org/postalCodeLookupJSON" $ .ajax (url: baseUrl, data: "postnummer": zip, "land": " oss ", skriv:" GET ", dataType:" JSONP "). gjort (this.fetched.bind (this)); ;
Her abonnerer vi på endringer av postnummeret. Når det endres, vil zipChanged
Metoden vil bli kalt. De zipChanged
Metoden vil sjekke for å se om lengden på verdien av zip er 5
. Når den når 5
, de hente
Metoden vil bli kalt. Her er hvor Sinon-stuben kommer inn for å spille. På dette punktet, $ .ajax
er faktisk en Sinon stub. Så calledOnce
vil da være ekte
i testen.
Den endelige testen vi skal skrive er for når dataene kommer tilbake fra Geonames-tjenesten:
test ("skal sette byinformasjon basert på søkeresultat", funksjon () var adresse = ny AddressViewModel (); adress.fetched (postnummer: [adminCode1: "foo", adminName2: "bar", placeName: "bam "), lik (address.city ()," bam "); lik (adress.state ()," foo "); lik (address.county ()," bar "););
Denne testen vil teste hvordan dataene fra serveren blir satt inn på AddressViewmodel
. Kjør det, se noe rødt. Gjør det nå grønt:
AddressViewModel.prototype.fetched = funksjon (data) var cityInfo; hvis (data.postalcodes && data.postalcodes.length === 1) cityInfo = data.postalcodes [0]; this.city (cityInfo.placeName); this.state (cityInfo.adminCode1); this.county (cityInfo.adminName2); ;
Den hentede metoden sørger bare for at det finnes en rekke postalcodes
i dataene fra serveren, og setter deretter de tilsvarende egenskapene på viewModel
.
Se hvor lett dette er nå? Når du får strømmen til å gjøre dette nede, vil du finne deg selv nesten ikke vil ikke TDD igjen. Du ender med fine, små funksjoner som kan testes. Du tvinger deg til å tenke på hvordan koden din interagerer med sine avhengigheter. Og nå har du en serie tester som skal løpe når et annet nytt krav er lagt til koden. Selv om du savner noe og det er en feil i koden, er du nå i stand til å bare legge til en ny test i suiten, for å bevise at du har løst feilen! Det er faktisk en slags addicting.
Testdekning gir en enkel måte å vurdere hvor mye av koden du har testet ved en enhetstest. Det er ofte vanskelig og ikke verdt det å nå 100% dekning, men gjør det du kan for å få det så høyt som mulig.
En av de nyere og enklere dekningsbiblioteker kalles Blanket.js. Å bruke den med QUnit er død enkel. Bare ta tak i koden rett ut av hjemmesiden deres eller installer den med Bower. Legg deretter til teppe som et bibliotek i bunnen av din qunit.html
fil, og legg deretter til data-deksel
til alle filene du vil ha dekningstester på.
Ferdig. Super enkelt, og nå får du et alternativ i QUnit-løperen for å vise dekning:
I dette eksemplet kan du se at testdekning ikke er helt 100%, men i dette tilfellet siden det ikke er mye kode, blir det enkelt å støte opp det. Du kan faktisk bor ned og se de nøyaktige funksjonene som ikke er dekket ennå:
Her i dette tilfellet, FormViewModel
ble aldri opprettet i testene og mangler derfor testdekning. Du kan da bare legge til en ny test som skaper en forekomst av FormViewModel
, og kanskje skrive et påstand som kontrollerer at adresse
Eiendommen er til stede og er en tilfelle av
de AddressViewModel
.
Du vil da få glede av å se 100% testdekning.
Etter hvert som applikasjonene dine blir større og større, er det fint å kunne kjøre litt statisk analyse på JavaScript-koden din. Et flott verktøy for å kjøre analyse på JavaScript kalles Plato.
Du kan kjøre plato
ved å installere den via NPM
med:
npm installere -g plato
Så kan du løpe plato
på en katalog med JavaScript-kode:
plato -r -d js / app rapporter
Dette vil kjøre Plato på alle JavaScript som ligger på "js / app
"og levere resultatene inn i rapporter
. Plato kjører alle slags beregninger på koden din, inkludert gjennomsnittlige kodelinjer, en beregnet vedlikeholdsscore, JSHint, vanskelige, estimerte feil og mer.
Det er ikke mye å se i det forrige bildet, ganske enkelt fordi for koden vi har jobbet med, er det bare én fil, men når du kommer inn i arbeid med et stort program som har mange filer og kodelinjer , finner du informasjonen den gir deg svært nyttig.
Det følger til og med på alle tidene du har kjørt det, slik at du kan se hvordan statistikken endres over tid.
Mens du tester klientsiden, virker som et vanskelig forslag, er det så mange gode verktøy å bruke disse dager for å gjøre det super enkelt. Denne artikkelen skraper knapt overflaten av alle tingene der ute i dag for å gjøre klientsiden testing enkel. Det kan være en kjedelig oppgave, men du finner at til slutt har fordelene ved å ha en testpakke og testbar kode langt oppveier det. Forhåpentligvis med trinnene som er skissert her, vil du kunne begynne å teste klientsidekoden raskt.