Testarea Ember.js

Când am început să joc cu Ember.js cu aproape un an în urmă, povestea de testare a lăsat ceva de dorit. Ați putea să testați un obiect fără probleme, dar un test de unitate este doar o modalitate de a obține feedback atunci când construiți un produs software. În plus față de testele de unitate, am dorit o modalitate de a verifica integrarea mai multor componente. Deci, ca majoritatea oamenilor care testează aplicații JavaScript bogate, am ajuns pentru mama tuturor instrumentelor de testare, Selenium.

Acum, înainte de a le bash, fără o introducere corectă, merită menționat faptul că Selenium este o modalitate foarte bună de a verifica dacă întreaga aplicație web funcționează cu o bază de date completă de producție și toate dependențele dvs. de producție etc. Și dintr-o perspectivă QA, poate fi o resursă excelentă pentru echipele care au nevoie de teste de acceptare UI de la sfârșitul la sfârșit.

Dar, în timp, o suită de testări aparent mică, construită pe Selenium, poate începe să tragă viteza echipei tale într-un ritm de melci. O modalitate ușoară de a reduce această durere este de a evita construirea unei aplicații mari în primul rând. Dacă construiți în schimb o mână de aplicații web mai mici, ar putea ajuta să vă menținem pe timp de plutire pentru un pic mai mult pentru că nici o construcție individuală nu va zdrobi echipa, pe măsură ce creșteți.

Dar chiar și pe un mic proiect, problema reală cu Selenium este că nu face parte din procesul de dezvoltare bazat pe teste. Când fac roșu / verde / refactor nu am timp pentru feedback lent sub orice formă. Aveam nevoie de o modalitate de a scrie atât teste de unitate, cât și de integrare, care să ofere feedback rapid pentru a mă ajuta să modelez software-ul pe care îl scriam într-un mod mai iterativ. Dacă utilizați o versiune Ember.js> = RC3, aveți noroc deoarece scrierea unei unități sau testul de integrare este o plimbare în partea.


Instalarea Runner-ului de testare

Acum, că putem scrie teste JavaScript pentru aplicația noastră, cum le executăm? Majoritatea dezvoltatorilor încep să utilizeze browserul în mod direct, dar pentru că am vrut să pot executa ceva fără fir de la linia de comandă într-un mediu CI cu un ecosistem bogat plin de pluginuri, m-am uitat la Karma.

Ceea ce mi-a plăcut despre Karma este că vrea doar să fie alergătorul tau. Nu contează ce cadru de testare JavaScript folosiți sau ce cadru MVC pe partea clientului îl utilizați. Este simplu să începeți și să scrieți teste care se execută împotriva producției dvs. Aplicația Ember.js este doar câteva linii de configurare.

Dar, înainte de a putea configura Karma, trebuie să-l instalăm folosind npm. Vă recomandăm să îl instalați local pentru a vă putea păstra modulele npm izolate pentru fiecare proiect. Pentru a face acest lucru, adăugați un fișier numit package.json"la rădăcina proiectului dvs. care arată ceva de genul celor de mai jos.

 "dependențe": "karma-qunit": "*", "karma": "0.10.2"

Acest exemplu va necesita atât Karma, cât și un plugin pentru Qunit. După ce salvați package.json fișierul de mai sus, reveniți la linia de comandă și tastați npm install pentru a trage în jos modulele Nod necesare.

După terminarea instalării npm, veți vedea acum un nou dosar cu numele node_modules în rădăcina proiectului. Acest director conține tot codul JavaScript pe care tocmai l-am tras cu npm, inclusiv Karma și pluginul Qunit. Dacă te descurci și mai departe node_modules / karma / bin / veți vedea executabilul Karma. Vom folosi aceasta pentru a configura alergatorul de test, pentru a executa testele de la linia de comandă etc.


Configurați testatorul de test

Apoi trebuie să configuram karma astfel încât să știe cum să execute testele Qunit. Tip karma init de la rădăcina proiectului. Vi se va solicita o listă de întrebări. Primul va întreba ce cadru de testare doriți să îl utilizați, lovit Tab până veți vedea qunit, apoi lovit introduce. Răspunsul următor Nu la întrebarea Require.js, deoarece nu o vom folosi pentru această aplicație de probă. Tab până veți vedea PhantomJS pentru a treia întrebare și va trebui să loviți introduce de două ori, deoarece permite mai multe opțiuni aici. În ceea ce privește restul, lăsați-le la opțiunea lor implicită.

Când ați terminat, ar trebui să vedeți că Karma a generat un fișier de configurare numit karma.conf.js în rădăcină sau în proiect. Dacă doriți să citiți mai multe despre diferitele opțiuni pe care le suportă Karma, este posibil să găsiți comentariile utile. De dragul acestui exemplu, am o versiune simplificată a fișierului de configurare pentru a păstra lucrurile începător prietenos.

Dacă doriți să urmați, ștergeți fișierul de configurare generat și înlocuiți-l cu acesta.

 modul.exports = funcția (karma) karma.set (basePath: 'js', fișierele: "vendor / jquery / jquery.min.js", vendor / handlebars / handlebars.js, "," vendor / jquery-mockjax / jquery.mockjax.js "," app.js "," teste / *. js "], logLevel: karma.LOG_ERROR, browsere: [PhantomJS] adevărat, autoWatch: false, cadre: ["qunit"]); ;

Acest lucru ar trebui să fie destul de similar cu ceea ce a generat Karma mai devreme, tocmai am eliminat toate comentariile și am tăiat câteva opțiuni pe care nu le pasă acum. Pentru a scrie primul test de unitate, a trebuit să-i spun lui Karma ceva mai mult despre structura proiectului.

În partea de sus a fișierului de configurare, veți vedea că l-am setat basePath la js deoarece toate activele JavaScript se află sub acest dosar în proiect. Apoi, i-am spus lui Karma unde poate găsi fișierele JavaScript necesare pentru a testa aplicația noastră simplă. Acestea includ jQuery, Handlebars, Ember.js și app.js fișierul însuși.


Scrierea testului pentru prima unitate

Acum putem adăuga primul fișier de testare a unității la proiect. Mai întâi faceți un nou dosar numit teste și să-l cuibăresc sub js pliant. Adăugați un fișier în acest nou director numit unit_tests.js care arată cam așa.

 test ('hello world', funcția () egală (1, 1, ""););

Acest test nu face încă nimic valoros, dar ne va ajuta să verificăm că avem totul conectat cu Karma pentru al executa corect. Observați în Karma fișiere secțiune, am adăugat deja js / teste director. În acest fel, Karma va trage în fiecare fișier JavaScript pe care îl folosim pentru a testa aplicația noastră, înainte.

Acum, când avem Karma configurat corect, executați testele qunit din linia de comandă utilizând ./ node_modules / karma / bin / karma începe.

Dacă aveți toate setările corecte, ar trebui să vedeți Karma să execute un test și să aibă succes. Pentru a verifica dacă a executat testul pe care tocmai l-am scris, mergeți să nu reușiți prin modificarea instrucțiunii equals. De exemplu, puteți face următoarele:

 test ('hello world', funcția () egală (1, 2, "boom"););

Dacă puteți să nu reușiți și să treceți din nou, este timpul să scrieți un test cu un scop mai mic.


Aplicația de probă

Dar, înainte de a începe, permiteți să discutați despre exemplul aplicației utilizate în cadrul acestui post. În ecranul de mai jos, vedeți că avem o rețea foarte simplă de utilizatori. În tabelul HTML, fiecare utilizator este afișat prin prenume, împreună cu un buton pentru a șterge acel utilizator. În partea de sus a aplicației veți vedea o intrare pentru numele, prenumele și în final un buton care va adăuga un alt utilizator în tabel când a fost făcut clic.

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

Aplicația de exemplu are trei probleme. În primul rând, dorim să afișăm numele și prenumele utilizatorului, nu doar primul nume. Apoi, când faceți clic pe un buton de ștergere, acesta nu va elimina de fapt utilizatorul. În sfârșit, când adăugați un nume, numele de familie și faceți clic pe adăugați, acesta nu va pune alt utilizator în tabel.

La suprafață, schimbarea numelui complet pare a fi cea mai simplă. De asemenea, sa dovedit a fi un exemplu excelent care arată când trebuie să scrieți un test de unitate, un test de integrare sau ambele. În acest exemplu, cea mai rapidă modalitate de obținere a feedback-ului este de a scrie un test unic simplu care afirmă că modelul are o proprietate calculată Numele complet.


Unitatea de testare a proprietății computerizate

Unitatea de testare a unui obiect ember este ușor, creați pur și simplu o nouă instanță a obiectului și cereți Numele complet valoare.

 test ('fullName proprietatea returnează atât primul și ultimul', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups' ), egal (rezultat, "facturi torane", "nume complet" + rezultat););

În continuare, dacă vă întoarceți la linia de comandă și rulați ./ node_modules / karma / bin / karma începe, ar trebui să prezinte un test de eșec cu un mesaj util care să descrie Numele complet ca nedefinit în prezent. Pentru a rezolva acest lucru, trebuie să deschidem app.js fișier și adăugați o proprietate calculată la modelul care returnează un șir de valori combinate de nume și prenume.

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

Dacă reveniți la linia de comandă și rulați ./ node_modules / karma / bin / karma începe ar trebui să vedeți acum un test de test. Puteți extinde acest exemplu scriind câteva teste pentru alte unități pentru a arăta că proprietatea calculată ar trebui să se schimbe atunci când numele sau prenumele sunt actualizate pe model.

 test ('fullName proprietatea returnează atât primul și ultimul', function () var person = App.Person.create (firstName: 'toran', lastName: 'billups' ), egal (rezultat, "facturi torane", "nume complet" + rezultat);); test ('fullName actualizări de proprietate atunci când firstName este schimbat', funcție () var person = App.Person.create (firstName: 'toran', lastName: 'billups' ), egal (rezultă, "facturi de torane", "fullName a fost" + rezultat); person.set ('firstName', 'wat' ', "fullName a fost" + rezultat);); test ('fullName actualizări de proprietate atunci când lastName este schimbat', funcție () var person = App.Person.create (firstName: 'toran', lastName: 'billups' ), egal (rezultatul, 'toran tbozz', 'fullName a fost' + rezultat), person.set ('lastName', 'tbozz' ', "fullName a fost" + rezultat););

Dacă adăugați aceste două teste suplimentare și executați toate cele trei din linia de comandă, ar trebui să aveți două eșecuri. Pentru a trece toate cele trei teste, modificați proprietatea calculată pentru a asculta modificări atât pe nume, cât și pe numele de familie. Acum, dacă alergi ./ node_modules / karma / bin / karma începe din linia de comandă, ar trebui să aveți trei teste trecute.

 App.Person = Ember.Object.extend (firstName: ", lastName:", plinName: function () var primaName = this.get ('firstName'); var lastName = this.get firstName + "+ numeName; .property ('firstName', 'lastName'));

Adăugați Pre-Procesorul Karma-Ember și Configurați-l

Acum că avem o proprietate calculată asupra modelului, trebuie să ne uităm la șablonul în sine, deoarece în prezent nu folosim noul model Numele complet proprietate. În trecut, ar fi trebuit să sârmuiți totul pe cont propriu sau să folosiți Selenium pentru a verifica dacă șablonul este redat corect. Dar, cu testarea ember, puteți integra acum acest test adăugând câteva linii de JavaScript și un plugin pentru Karma.

Mai întâi deschideți package.json fișier și adăugați dependența karma-ember-preprocesor. După ce actualizați package.json fișier, nu npm install din linia de comandă pentru a trage acest lucru în jos.

 "dependențe": "karma-ember-preprocesor": "*", "karma-qunit": "*", "karma": "0.10.2"

Acum, când aveți instalat pre-procesorul, trebuie să îl facem pe Karma conștient de fișierele șablonului. În fișiere secțiune din karma.conf.js fișier adăugați următoarele pentru a le spune Karma despre șabloanele Handlebars.

 modul.exports = funcția (karma) karma.set (basePath: 'js', fișierele: "vendor / jquery / jquery.min.js", vendor / handlebars / handlebars.js, ember.js "," vendor / jquery-mockjax / jquery.mockjax.js "," app.js "," teste / *. js "," șabloane / *. ['PhantomJS'], singleRun: adevărat, autoWatch: false, cadre: ["qunit"]); ;

Apoi trebuie să-i spunem lui Karma ce să facă cu aceste fișiere de ghidon, pentru că, din punct de vedere tehnic, dorim să precomprimăm fiecare șablon înainte de a fi predat PhantomJS. Adăugați configurația preprocesorului și indicați ceva cu o extensie de fișier din * .handlebars la preprocesorul ember. De asemenea, trebuie să adăugați configurația pluginurilor pentru a înregistra pre-procesorul ember (împreună cu câteva altele care în mod obișnuit sunt incluse în configurația implicită a lui Karma).

 modul.exports = funcția (karma) karma.set (basePath: 'js', fișierele: "vendor / jquery / jquery.min.js", vendor / handlebars / handlebars.js, ember.js "," vendor / jquery-mockjax / jquery.mockjax.js "," app.js "," teste / *. js "," șabloane / *. ['Karma-qunit', 'karma-chrome-launcher', 'karma-ember-preprocessor', 'karma- lansatoare de fantome], preprocesoare: "** / *. ghidon": "ember"); ;

Integrarea Testarea șablonului legat de date

Acum, când avem configurația configurației Karma pentru testarea integrării, adăugați un nou fișier numit integration_tests.js sub teste pliant. În interiorul acestui dosar trebuie să adăugăm un test simplu pentru a demonstra că putem apăra întreaga aplicație Ember.js fără eroare. Adăugați un test simplu qunit pentru a vedea dacă putem lovi '/' rută și obțineți HTML-ul de bază returnat. Pentru testul inițial, afirmăm doar că masa tag-ul există în codul HTML care a fost generat.

 test ('hello world', functie () App.reset (); vizitati ("/").

Observați că folosim câțiva ajutoare care sunt construite în testări de tip ember vizita și găsi. vizita helper este o modalitate prietenoasă de a spune aplicației ce stat să fie în timpul execuției. Acest test începe la '/' pentru că acolo unde modelele Oamenii se leagă de șablon și se generează tabelul HTML. găsi helper este o modalitate rapidă de a căuta elemente în DOM utilizând selectori CSS ca și cu jQuery pentru a verifica ceva despre marcare.

Înainte de a putea executa acest test, trebuie să adăugăm un fișier de test helper care va injecta ajutorul de testare și va stabili un element rădăcină generic. Adăugați codul de mai jos, într-un fișier numit integration_test_helper.js în același teste director. Acest lucru va asigura ca aplicatia noastra are ajutorul de testare la timpul de executie.

 document.write (“
„); App.rootElement = '# ember-testing'; App.setupForTesting (); App.injectTestHelpers (); funcția există (selector) return !! find (selector) .length;

Acum, din linia de comandă, trebuie să puteți executa testul de integrare de mai sus. Dacă aveți un test de deplasare, eliminați tabelul din șablonul ghidon pentru a nu reuși (doar pentru a demonstra că Ember generează codul HTML utilizând șablonul).

Acum, că avem setările pentru testele de integrare, este timpul să scriem unul care susține afișarea fiecărui utilizator Numele complet în locul lor Nume. Vrem să afirmăm mai întâi că avem două rânduri, câte unul pentru fiecare persoană.

 test ('hello world', functie () App.reset (); vizita ("/"). );););

Notă: În prezent, aplicația întoarce date greu codate pentru a păstra totul simplu în acest moment. Dacă sunteți curios de ce avem două persoane, iată-ne găsi metoda pe model:

 App.Person.reopenClass (persoane: [], găsiți: function () var first = App.Person.create (firstName: 'x', lastName: 'y'); (nume_firma: 'x', lastName: 'y'); this.people.pushObject (primul); this.people.pushObject (last); return this.people;);

Dacă facem testele acum, ar trebui să trecem totul, deoarece două persoane sunt returnate așa cum ne-am fi așteptat. Apoi, trebuie să luăm celula de masă care arată numele persoanei și afirmă că folosește Numele complet proprietate în loc de doar Nume.

 test ('hello world', functie () App.reset (); vizita ("/"). ); var fullName = find ("tabelul tr: eq (0) td: eq (0)") ););

Dacă executați testul de mai sus, ar trebui să vedeți un test de eșec deoarece nu am actualizat încă șablonul de utilizat Numele complet. Acum, că avem un test de eșec, actualizați șablonul de folosit Numele complet și executați testele utilizând ./ node_modules / karma / bin / karma începe. Acum ar trebui să aveți o serie de teste de integrare și integrare.


Trebuie să scriu unități sau teste de integrare?

Dacă vă întrebați "când trebuie să scriu un test de unitate vs. un test de integrare?", Răspunsul este pur și simplu: ce va fi mai puțin dureros? Dacă scrierea unui test de unitate este mai rapidă și explică problema mai bine decât un test de integrare mult mai mare, atunci spun că scrieți testul unității. Dacă testele unității par mai puțin valoroase deoarece faceți CRUD de bază și comportamentul real se află în interacțiunea dintre componente, spun că scrieți testul de integrare. Deoarece testele de integrare scrise cu testul "ember" sunt rapide, ele fac parte din ciclul de feedback al dezvoltatorului și ar trebui să fie utilizate în mod similar testului unității când are sens.

Pentru a arăta un test de integrare CRUD în acțiune, scrieți următorul test pentru a demonstra adăuga butonul pune persoana în colecție și că un rând nou este redat în șablonul ghidonului.

 test ('add adaugă o altă persoană la tabela html', funcția () App.Person.people = []; App.reset (); vizitați ("/"). ("tabelul tr"), lungimea egală (rânduri, 2, "tabelul a avut" + rânduri + "rânduri), fillIn (". ()), apoi (funcția () egal (find ("table tr"), lungimea, 3, "tabela de oameni nu a fost completă" tr: eq (2) td: eq (0) ") text ()," foo bar "," numele complet al persoanei a fost incorect ");

Începeți prin a spune testului în ce stare doriți să lucrați, apoi folosiți a umple helper, adăugați un nume și prenume. Acum, dacă faceți clic pe a depune butonul trebuie să adauge acea persoană la tabela HTML, astfel încât în ​​întoarcere atunci putem afirma că în tabelul HTML există trei persoane. Rulați acest test și ar trebui să eșueze deoarece controlerul Ember nu este complet.

Pentru a trece testul, adăugați următoarea linie la PeopleController

 App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = firstName: this.get ('firstName'), lastName: this.get ('lastName'); .add (persoană););

Acum, dacă executați testele folosind ./ node_modules / karma / bin / karma începe ar trebui să arate trei persoane în HTML-ul redat.

Ultimul test este ștergerea, notați butonul pentru un anumit rând și faceți clic pe el. În cele ce urmează atunci verificăm pur și simplu că o persoană mai mică este afișată în tabelul HTML.

 test ("ștergeți va elimina persoana pentru un rând dat", funcția () App.Person.people = []; App.reset (); vizitați ("/"). ("table tr"), lungime, egal (rânduri, 2, "tabelul a avut" + rânduri + "rânduri"); (găsiți "tabelul tr"), lungimea, 1, "tabelul de oameni nu a fost completat););)

Pentru a obține această trecere, pur și simplu adăugați următoarea linie la PeopleController:

 App.PeopleController = Ember.ArrayController.extend (actions: addPerson: function () var person = firstName: this.get ('firstName'), lastName: this.get ('lastName'); .add (persoană);, deletePerson: funcția (persoană) App.Person.remove (persoană););

Rulați testele din linia de comandă și ar trebui să aveți din nou o serie de teste trecute.


Concluzie

Așa că ne împachetează aplicația de probă. Simțiți-vă liber să puneți întrebările în comentarii.

Bonus: Dar deja folosesc Grunt ...

Dacă preferați să utilizați Grunt în locul preprocesorului karma-ember, pur și simplu eliminați configurația plugin-urilor și preprocesoarelor. De asemenea, eliminați șabloane / *. ghidonul din secțiunea de fișiere, deoarece Karma nu va trebui să precomprimă șabloanele. Aici este un mod simplificat karma.conf.js care funcționează atunci când utilizați grunt pentru a precompila șabloanele ghidonului.

 modul.exports = functie (karma) karma.set (basePath: 'js', fisiere: ["lib / deps.min.js" : karma.LOG_ERROR, browsere: ['PhantomJS'], singleRun: true, autoWatch: false, cadre: ["qunit"]); ;

Si asta e!

Cod