Ember.js Testing

Da jeg begynte å leke med Ember.js for nesten et år siden, viste testbarhetshistorien noe å være ønsket. Du kan prøve å teste et objekt uten problemer, men en enhetstest er bare en måte å få tilbakemelding når du bygger et programvareprodukt. I tillegg til enhetstester ønsket jeg en måte å verifisere integrering av flere komponenter på. Så som de fleste testet rike JavaScript-applikasjoner, nådde jeg for moren til alle testverktøy, Selen.

Nå før jeg bash det, uten en riktig introduksjon, er det verdt å nevne at Selen er en fin måte å verifisere hele webapplikasjonen din, fungerer med en full produksjonslignende database og alle dine produksjonsavhengigheter, etc. Og fra et QA-perspektiv, er dette verktøyet kan være en god ressurs for lag som trenger end-to-end UI-aksepttester.

Men over tid kan en tilsynelatende liten testpakke bygget på Selen begynne å dra hastigheten til laget ditt til et snegltakthastighet. En enkel måte å redusere denne smerten på er å unngå å bygge en stor applikasjon i utgangspunktet. Hvis du bygger en håndfull mindre webapplikasjoner i stedet, kan det være med til å holde deg flytende for litt lenger fordi ingen enkeltbygging vil knuse laget, mens du vokser.

Men selv på et lite prosjekt er det virkelige problemet med Selen at det ikke er en del av den testdrevne utviklingsprosessen. Når jeg gjør rød / grønn / refactor, har jeg ikke tid til sakte tilbakemelding i noen form. Jeg trengte en måte å skrive både enhet og integrasjonstester på som ville gi rask tilbakemelding for å hjelpe meg til å forme programvaren jeg skrev på en mer iterativ måte. Hvis du bruker en versjon av Ember.js> = RC3, har du lykke, fordi du skriver en enhet eller integreringstest er en tur i delen.


Installere testløperen

Nå som vi kan skrive JavaScript-tester for vår søknad, hvordan utfører vi dem? De fleste utviklere starter ut med nettleseren direkte, men fordi jeg ønsket noe jeg kunne utføre headless fra kommandolinjen i et CI miljø med et rikt økosystem fullt av plugins, så jeg Karma.

Det jeg likte Karma er at det bare vil være testløperen din. Det bryr seg ikke om hvilken JavaScript-testramme du bruker eller hvilken klientside MVC-rammeverk du bruker. Det er enkelt å komme i gang med og skrive tester som utfører mot produksjonen din Ember.js-applikasjonen er bare noen få linjer med konfigurasjon.

Men før vi kan konfigurere Karma, må vi installere den ved hjelp av npm. Jeg anbefaler at du installerer den lokalt, slik at du kan holde npm-modulene dine isolert per prosjekt. For å gjøre dette, legg til en fil med navnet package.json'til roten til prosjektet ditt som ser noe ut som nedenfor.

 "avhengigheter": "karma-qunit": "*", "karma": "0.10.2"

Dette eksemplet krever både Karma og et plugin for QUnit. Etter at du har lagret package.json filen ovenfor, slipp tilbake til kommandolinjen og skriv inn npm installasjon å trekke ned de nødvendige nodemodulene.

Etter at npm-installasjonen er fullført, vil du nå se en ny mappe med navnet node_modules i roten til prosjektet ditt. Denne mappen inneholder all JavaScript-koden vi nettopp trakk ned med npm, inkludert Karma og QUnit-plugin. Hvis du driller deg enda lenger til node_modules / karma / bin / Du vil se Karma kjørbar. Vi skal bruke dette til å konfigurere testløperen, utføre tester fra kommandolinjen, osv.


Konfigurer testløperen

Neste må vi konfigurere karma så det vet hvordan å utføre QUnit-testene. Type karma init fra roten til prosjektet. Du vil bli bedt om en liste over spørsmål. Den første vil spørre hvilken testramme du vil bruke, hit Tab til du ser qunit, deretter treffer Tast inn. Neste svar Nei til Question.js-spørsmålet, da vi ikke vil bruke det til denne prøveapplikasjonen. Tab til du ser PhantomJS for det tredje spørsmålet, og du må treffe Tast inn to ganger da det tillater flere alternativer her. Som for resten, bare la dem stå på standardinnstillingen.

Når du er ferdig, bør du se at Karma har generert en konfigurasjonsfil som heter karma.conf.js i roten eller prosjektet ditt. Hvis du vil lese mer om de ulike alternativene som Karma støtter, kan du finne kommentarene som er nyttige. For dette eksempelets skyld har jeg en forenklet versjon av konfigurasjonsfilen for å holde ting nybegynnerlig.

Hvis du vil følge med, sletter du den genererte konfigurasjonsfilen og erstatter den med denne.

 modul.exports = funksjon (karma) karma.set (basePath: 'js', filer: ["leverandør / jquery / jquery.min.js", "leverandør / styrer / handlebars.js", "leverandør / ember / ember.js "," leverandør / jquery-mockjax / jquery.mockjax.js "," app.js "," tester / *. js "), logLevel: karma.LOG_ERROR, nettlesere: ['PhantomJS'], singleRun: true, autoWatch: false, frames: ["qunit"]); ;

Dette skal være ganske lik det som Karma genererte tidligere, jeg har nettopp fjernet alle kommentarene og kuttet ut noen alternativer som vi ikke bryr oss om akkurat nå. For å skrive den første enhetstesten måtte jeg fortelle Karma litt mer om prosjektstrukturen.

På toppen av konfigurasjonsfilen ser du at jeg har satt basePath til js fordi alle JavaScript-eiendelene lever under denne mappen i prosjektet. Deretter fortalte jeg Karma hvor det kan finne JavaScript-filene som kreves for å teste vår enkle applikasjon. Dette inkluderer jQuery, Handlebars, Ember.js og app.js filen selv.


Skrive den første enhetstesten

Nå kan vi legge til den første enhetstestfilen til prosjektet. Først må du lage en ny mappe som heter tester og nest den under js mappe. Legg til en fil i denne nye katalogen som heter unit_tests.js Det ser noe ut som dette.

 test ('hallo verden', funksjon () like (1, 1, "";);

Denne testen gjør ikke noe verdifullt ennå, men det vil hjelpe oss med å verifisere at vi har alt koblet opp med Karma for å utføre det riktig. Legg merke til i Karma filer delen, har vi allerede lagt til JS / tester katalogen. På denne måten vil Karma trekke inn alle JavaScript-filer vi bruker til å teste vår søknad med, fremover.

Nå som vi har Karma konfigurert riktig, utfør qunit tester fra kommandolinjen ved hjelp av ./ node_modules / karma / bin / karma start.

Hvis du har alt riktig konfigurert, bør du se Karma utføre en test og det lykkes. For å bekrefte det utført testen vi nettopp har skrevet, gjør det mislykkes ved å endre likestillingen. For eksempel kan du gjøre følgende:

 test ('hallo verden', funksjon () like (1, 2, "boom"););

Hvis du kan mislykkes og gjøre det igjen, er det på tide å skrive en prøve med litt mer hensikt.


Sample Application

Men før vi begynner, kan vi diskutere prøveapplikasjonen som brukes i dette innlegget. I skjermbildet under ser du at vi har et veldig enkelt rutenett av brukere. I HTML-tabellen vises hver bruker etter fornavn sammen med en knapp for å slette den aktuelle brukeren. Øverst på applikasjonen vil du se et innspill for fornavn, etternavn og til slutt en knapp som vil legge til en annen bruker i tabellen når du klikker.

https://dl.dropboxusercontent.com/u/716525/content/images/2013/pre-tuts.png

Eksempelprogrammet har tre problemer. Først vil vi vise brukerens for- og etternavn, ikke bare fornavnet. Deretter fjerner du ikke brukeren når du klikker en sletteknapp. Og til slutt, når du legger til et fornavn, etternavn og klikk legg til, vil det ikke sette en annen bruker inn i bordet.

På overflaten ser det ut til å være det enkleste navnet endringen. Det viste seg også å være et godt eksempel som viser når du skal skrive en enhetstest, en integreringstest eller begge deler. I dette eksemplet er den raskeste måten å få tilbakemelding på å skrive en enkel enhetstest som hevder at modellen har en beregnet egenskap fullt navn.


Enhetstesting av den beregnede eiendommen

Enhetstesting av et ember-objekt er enkelt, du oppretter bare en ny forekomst av objektet og ber om fullt navn verdi.

 test ('fullName-egenskap returnerer både første og siste', funksjon () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); lik (resultat, "toran billups", "fullName var" + resultat););

Neste hvis du går tilbake til kommandolinjen og kjører ./ node_modules / karma / bin / karma start, det skal vise en feiltest med en nyttig beskjed som beskriver fullt navn som udefinert i øyeblikket. For å fikse dette må vi åpne app.js fil og legg til en beregnet eiendom til modellen som returnerer en streng av de kombinerte for- og etternavnet verdiene.

 App.Person = Ember.Object.extend (firstName: ", lastName:", fullName: funksjon () var firstName = this.get ('firstName'); var lastName = this.get ('lastName') firstName + "+ lastName; .property ());

Hvis du går tilbake til kommandolinjen og kjører ./ node_modules / karma / bin / karma start Du bør nå se en bestått enhetsprøve. Du kan utvide dette eksemplet ved å skrive noen andre enhetstester for å vise at den beregnede egenskapen skal endres når enten for- eller etternavnet er oppdatert på modellen.

 test ('fullName-egenskap returnerer både første og siste', funksjon () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ); lik (resultat, "toran billups", "fullName var" + resultat);); test ('fullName eiendomsoppdateringer når førstnavn er endret', funksjon () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ), lik (resultat, "toran billups", "fullName var" + resultat); person.set ('firstName', 'wat'); result = person.get ('fullName'); lik ',' fullName var "+ resultat);); test ('fullName-eiendomsoppdateringer når sistnavn er endret', funksjon () var person = App.Person.create (firstName: 'toran', lastName: 'billups'); var result = person.get ('fullName' ), like (resultat, 'toran billups', "fullName var" + resultat); person.set ('lastName', 'tbozz'); resultat = person.get ('fullName'); lik (resultat, 'toran tbozz ',' fullName var "+ resultat););

Hvis du legger til disse to ekstra tester og kjører alle tre fra kommandolinjen, bør du ha to feil. For å få alle tre tester passerer, endre den beregnede egenskapen for å lytte etter endringer på både fornavn og etternavn. Nå hvis du kjører ./ node_modules / karma / bin / karma start fra kommandolinjen, bør du ha tre beståttester.

 App.Person = Ember.Object.extend (firstName: ", lastName:", fullName: funksjon () var firstName = this.get ('firstName'); var lastName = this.get ('lastName') firstName + "+ lastName; .property ('firstName', 'lastName'));

Legg til karma-ember-preprosessoren og konfigurer den

Nå som vi har en beregnet eiendom på modellen, må vi se på malmen selv fordi vi for øyeblikket ikke bruker det nye fullt navn eiendom. Tidligere måtte du koble opp alt selv, eller bruke Selen til å bekrefte at malen blir gjengitt riktig. Men med ember-testing kan du nå integrering teste dette ved å legge til noen få linjer med JavaScript og et plugin for Karma.

Åpne først package.json fil og legg til karma-ember-preprocessoravhengigheten. Etter at du har oppdatert package.json fil, gjør npm installasjon fra kommandolinjen for å trekke dette ned.

 "avhengigheter": "karma-ember-preprocessor": "*", "karma-qunit": "*", "karma": "0.10.2"

Nå som du har forhåndsbehandleren installert, må vi gjøre Karma oppmerksom på malfiler. I filer delen av din karma.conf.js fil legg til følgende for å fortelle Karma om håndteringsmalene.

 modul.exports = funksjon (karma) karma.set (basePath: 'js', filer: ["leverandør / jquery / jquery.min.js", "leverandør / styrer / handlebars.js", "leverandør / ember / ember.js "," leverandør / jquery-mockjax / jquery.mockjax.js "," app.js "," tester / *. js "," maler / * .håndtak "], logLevel: karma.LOG_ERROR, nettlesere: ['PhantomJS'], singleRun: true, autoWatch: false, frames: ["qunit"]); ;

Neste må vi fortelle Karma hva du skal gjøre med disse styringsfilene, fordi vi teknisk sett ønsker å ha hver mal prekompilert før den overleveres til PhantomJS. Legg til preprosessorkonfigurasjonen og pek noe med en filtypenavn for * .handlebars på ember preprocessor. Du må også legge til plugin-konfigurasjonen for å registrere ember-forprosessoren (sammen med noen få andre som vanligvis følger med Karma standardkonfigurasjon).

 modul.exports = funksjon (karma) karma.set (basePath: 'js', filer: ["leverandør / jquery / jquery.min.js", "leverandør / styrer / handlebars.js", "leverandør / ember / ember.js "," leverandør / jquery-mockjax / jquery.mockjax.js "," app.js "," tester / *. js "," maler / * .håndtak "], logLevel: karma.LOG_ERROR, nettlesere: ['PhantomJS'], singleRun: true, autoWatch: falsk rammeverk: ["qunit"], plugins: ['karma-qunit', 'karma-krom-launcher', 'karma-ember-preprocessor', 'karma- phantomjs-launcher '], forprosessorer: "** / *. styrer":' ember '); ;

Integrasjonstesting av data-bundet mal

Nå som vi har Karma konfigurasjonsoppsett for integrasjonstesting, legg til en ny fil med navnet integration_tests.js under tester mappe. I denne mappen må vi legge til en enkel test for å bevise at vi kan stå opp hele Ember.js-søknaden uten feil. Legg til en enkel qunit test for å se om vi kan treffe '/' rute og få grunnleggende HTML returnert. For den første testen hevder vi bare at bord tag eksisterer i HTML som ble generert.

 test (hello world), funksjon () App.reset (); besøk ("/"). deretter (funksjon () ok (eksisterer ("tabell"));;;);

Legg merke til at vi bruker noen få hjelpere som er bygget inn i ember-testing som besøk og finne. De besøk Helper er en ember vennlig måte å fortelle søknaden hvilken tilstand som skal være under gjennomføringen. Denne testen starter på '/' rute fordi det er der folkemodellene blir bundet til malen og vårt HTML-tabell er generert. De finne hjelper er en rask måte å lete opp elementer i DOM ved hjelp av CSS-selektorer som du ville med jQuery for å bekrefte noe om merkingen.

Før vi kan kjøre denne testen, må vi legge til en testhjelpefil som vil injisere testhjelpene og angi et generisk rotelement. Legg til koden under, til en fil som heter integration_test_helper.js i samme tester katalogen. Dette vil sikre at søknaden vår har testhjelpene på utførelsestidspunktet.

 document.write ('
'); App.rootElement = '# ember-testing'; App.setupForTesting (); App.injectTestHelpers (); funksjon eksisterer (selector) return !! find (selector). lengde;

Nå fra kommandolinjen bør du kunne utføre integreringstesten ovenfor. Hvis du har en bestått test, fjerner du bordet fra håndtaksmalen for å gjøre det feil (bare for å bevise at Ember genererte HTML ved hjelp av den aktuelle malen).

Nå som vi har integreringstestoppsettet, er det på tide å skrive den som hevder at vi viser hver bruker fullt navn i stedet for deres fornavn. Vi vil først hevde at vi får to rader, en for hver person.

 test ("hej verden", funksjon () App.reset (); besøk ("/"). deretter (funksjon () var rader = finn ("tabell tr"). lengde; lik (rader, 2 rader );););

Merk: Programmet returnerer for tiden hardkodede data for å holde alt enkelt for øyeblikket. Hvis du er nysgjerrig på hvorfor vi får to personer, her er det finne metode på modellen:

 App.Person.reopenClass (people: [], find: function () var først = App.Person.create (firstName: 'x', lastName: 'y'); var sist = App.Person.create (firstName: 'x', lastName: 'y'); this.people.pushObject (first); this.people.pushObject (sist); returnere.personer;);

Hvis vi driver testene nå, bør vi fortsatt ha alt som går fordi to personer returneres som vi forventer. Deretter må vi få tabellcellen som viser personens navn og hevder at den bruker fullt navn eiendom i stedet for bare fornavn.

 test ("hej verden", funksjon () App.reset (); besøk ("/"). deretter (funksjon () var rader = finn ("tabell tr"). lengde; lik (rader, 2 rader ); fullName = "(" tabell tr: eq (0) td: eq (0) "). tekst (); lik (fullName," xy ";" den første tabellraden hadde fullName: "+ fullName); ););

Hvis du kjører testen ovenfor, bør du se en feiltest, fordi vi ennå ikke har oppdatert malen som skal brukes fullt navn. Nå som vi har en feiltest, oppdaterer du malen som skal brukes fullt navn og kjør testene med ./ node_modules / karma / bin / karma start. Du bør nå ha en bestått pakke med begge enheter og integreringstester.


Skal jeg skrive enhet eller integrasjonstester?

Hvis du spør deg selv, "når skal jeg skrive en unit test vs en integreringstest?", Er svaret rett og slett: Hva vil være mindre smertefullt? Hvis du skriver en enhetstest er raskere og det forklarer problemet bedre enn en mye større integreringstest, så sier jeg skrive enhetsprøven. Hvis enhetstestene virker mindre verdifulle fordi du gjør grunnleggende CRUD, og ​​den virkelige oppførselen er i samspillet mellom komponenter, sier jeg skrive integreringstesten. Fordi integreringstestene som er skrevet med ember-testing, er flammende raskt, er de en del av utviklerens tilbakemeldingssyklus og bør brukes på samme måte som en enhetstest når det er fornuftig.

For å vise en CRUD som integreringstest i aksjon, skriv følgende test for å bevise Legg til knappen setter personen inn i samlingen og at en ny rad blir gjengitt i håndtaksmalen.

 test ("legg til legger til en annen person i html-tabellen", funksjon () App.Person.people = []; App.reset (); besøk ("/"). deretter (funksjon () var rader = finn lengde like (rad 2, "tabellen hadde" + rader + "rader"); fillIn (".navn", "foo"); fillIn (".navn", "bar") , return, klikk ("send");). deretter (funksjon () lik (finn ("tabell tr"). lengde, 3, "folketabellen var ikke fullført"); tr: eq (2) td: eq (0) "). tekst ()," foo bar "," fullName for personen var feil ");;;

Begynn med å fortelle testen hvilken stat du vil jobbe med, og bruk deretter Fyll inn hjelper, legg til fornavn og etternavn. Nå hvis du klikker på sende inn knappen den skal legge til den personen i HTML-tabellen, så i retur deretter Vi kan hevde at det finnes tre personer i HTML-tabellen. Kjør denne testen, og den skal mislykkes fordi Ember-kontrolleren ikke er fullført.

For å få testen forbi, legg til følgende linje i PeopleController

 App.PeopleController = Ember.ArrayController.extend (handlinger: addPerson: function () var person = firstName: this.get ('firstName'), sistnavn: this.get ('lastName'); App.Person .add (person););

Nå hvis du kjører testene med ./ node_modules / karma / bin / karma start Det skal vise tre personer i gjengitt HTML.

Den siste testen er slettet, legg merke til at vi finner knappen for en bestemt rad, og klikk på den. I de neste deretter Vi kontrollerer bare at en mindre person er vist i HTML-tabellen.

 test ("slette vil fjerne personen for en gitt rad", funksjon () App.Person.people = []; App.reset (); besøk ("/"). deretter (funksjon () var rader = finn ("tabell"), lengde, lik (rad, 2, "tabellen hadde" + rader + "rader"); lengde 1, "bordet av mennesker var ikke fullført);;")))

For å få dette passerer, legger du bare til følgende linje til PeopleController:

 App.PeopleController = Ember.ArrayController.extend (handlinger: addPerson: function () var person = firstName: this.get ('firstName'), sistnavn: this.get ('lastName'); App.Person .add (person);, deletePerson: funksjon (person) App.Person.remove (person););

Kjør testene fra kommandolinjen, og du bør nok en gang bestå en prøvepakke.


Konklusjon

Så det bryter opp vår prøveapplikasjon. Ta gjerne spørsmål på ned i kommentarene.

Bonus: Men jeg bruker allerede Grunt ...

Hvis du foretrekker å bruke Grunt i stedet for karma-ember-preprosessoren, fjerner du bare pluginene og preprosessors konfigurasjon. Fjern også maler / *. styret fra filseksjonen som Karma trenger ikke å forkompilere malene. Her er en forenklet karma.conf.js Det fungerer når du bruker grunt til å forkompilere styreskjermene.

 module.exports = funksjon (karma) karma.set (basePath: 'js', filer: ["lib / deps.min.js", // bygget av din grunne oppgave "tester / *. js"), logLevel : karma.LOG_ERROR, nettlesere: ['PhantomJS'], singleRun: true, autoWatch: false, frames: ["qunit"]); ;

Og det er det!