Testarea JavaScript cu Jasmine

Știm cu toții că ar trebui să ne testeze codul, dar nu facem asta. Cred că este corect să spunem că cei mai mulți dintre noi l-au oprit deoarece, de nouă ori din zece, înseamnă învățarea unui alt concept. În acest tutorial, vă voi prezenta un cadru foarte mic pentru testarea cu ușurință a codului JavaScript.

Apropo, știți că puteți rezolva greșelile JavaScript rapid și ușor de către un expert pe Envato Studio?

ThemeManiac, de exemplu, vă va corecta erorile JavaScript sau problemele legate de compatibilitatea browserului pe site-ul web sau pe aplicația dvs. web. Corecțiile pot fi finalizate foarte rapid, pe baza complexității și a informațiilor disponibile. El poate, de asemenea, să vă reorganizeze scenariile și să facă o experiență complet nouă pentru utilizatori. El a finalizat peste 1.000 de locuri de muncă pe Envato Studio, cu 99% dintre clienți care îl recomandă.


Pasul 0: Înțelegerea BDD

Astăzi, vom învăța despre cadrul de testare Jasmine BDD. Dar ne oprim aici pentru un ocol, pentru a vorbi foarte scurt, despre BDD și TDD. Dacă nu sunteți familiarizați cu aceste acronime, ele sunt de acord Dezvoltarea condusă de comportament și Dezvoltare bazată pe dezvoltare. Sunt în mijlocul învățării despre ceea ce este în practică și despre cum sunt diferite, dar aici sunt câteva dintre diferențele de bază:

BDD și TDD? stați pentru Dezvoltarea condusă de comportament și Dezvoltare bazată pe dezvoltare.

TDD în cea mai simplă formă este doar:

  1. Scrieți testele
  2. Uita-te la eșec
  3. Fă-i treci
  4. Refactor
  5. Repeta

E destul de ușor de înțeles, eh?

BDD este un pic mai complex: așa cum înțeleg acum, nu cred că noi sau eu ca un singur dezvoltator putem să o practicăm pe deplin; este mai mult un lucru în echipă. Iată câteva dintre practicile BDD:

  • Stabilirea obiectivelor diferitelor părți interesate necesare pentru implementarea unei viziuni
  • Implicarea părților interesate în procesul de implementare prin dezvoltarea software-ului extern
  • Folosind exemple pentru a descrie comportamentul aplicației sau a unităților de cod
  • Automatizarea acestor exemple pentru a oferi feedback rapid și teste de regresie

Pentru a afla mai multe, puteți citi articolul extensiv Wikipedia (din care au fost luate aceste puncte).

Toate acestea pentru a spune că, în timp ce Jasmine se facturează ca un cadru BDD, o vom folosi într-un mod mai stil TDD. Asta nu înseamnă că o folosim greșit. Odată ce am terminat, veți putea testa JavaScript cu ușurință? și mă aștept să o faceți!


Pasul 1: Învățarea sintaxei

Jasmine ia multe indicii de la Rspec.

Dacă sunteți cu toții familiarizați cu Rspec, de facto BDD cadru, veți vedea că Jasmine are multe indicii de la Rspec. Testele de iasomie sunt în principal două părți: descrie blocuri și aceasta blocuri. Să vedem cum funcționează acest lucru.

Vom analiza câteva teste mai apropiate de viața reală în câteva, dar deocamdată vom păstra simplu:

descrie ("operatorul de adăugare a JavaScript", funcția () it ('adaugă două numere împreună', funcția () așteptați (1 + 2) .toEqual (3););

Amandoua descrie și aceasta funcțiile ia doi parametri: un șir de text și o funcție. Cele mai multe cadre de testare încearcă să citească cât mai mult posibil limba engleză și puteți vedea acest lucru cu Jasmine. Mai întâi, observați că șirul a trecut descrie iar șirul a trecut aceasta formați o propoziție (de feluri): "Operatorul JavaScript plus adaugă două numere împreună." Apoi continuăm să arătăm cum.

Înăuntru aceasta bloc, puteți scrie toate codurile de setare de care aveți nevoie pentru testul dvs. Nu avem nevoie de acest exemplu simplu. Odată ce sunteți pregătit să scrieți codul de testare real, veți începe cu aştepta funcția, trecând tot ceea ce testați. Observați cum se formulează o propoziție: ne așteptăm ca 1 + 2 să fie egală cu 3.?

Dar am trecut înainte de noi înșine. Așa cum am spus, indiferent de valoarea în care treci aştepta va fi testat. Metoda pe care o apelați, pe valoarea returnată din aştepta, va fi determinată de testul care se execută. Acest grup de metode este numit "matchers", iar noi vom analiza mai multe dintre ele astăzi. În acest caz, folosim la egal matcher, care verifică dacă valoarea a trecut aştepta și valoarea trecută la egal sunt aceeași valoare.

Cred că sunteți gata să vă duceți la nivelul următor, așa că să organizăm un simplu proiect folosind Jasmine.


Pasul 2: Configurarea unui proiect

Jasmine poate fi utilizată singură; sau îl puteți integra cu un proiect Rails. Vom face primul. În timp ce Jasmine poate rula în afara browserului (cred Nod, printre alte locuri), putem obține un șablon foarte frumos mic cu descărcare.

Deci, mergeți la pagina de descărcare independentă și obțineți cea mai recentă versiune. Ar trebui să obțineți ceva de genul:

Veți găsi fișierele actuale ale cadrului Jasmine în lib pliant. Dacă preferați să structurați proiectele în mod diferit, vă rugăm să faceți acest lucru; dar o vom păstra pentru moment.

Există de fapt un exemplu de cod conectat în acest șablon de proiect. Actualul? JavaScript (codul pe care dorim să îl testați) poate fi găsit în src subdirector; o să ne punem pe acolo acolo în curând. Codul de testare - Specificatii-mergeți în spec pliant. Nu vă faceți griji cu privire la SpecHelper.js fișier încă; ne vom întoarce la asta.

Acea SpecRunner.html fișierul este ceea ce rulează testele într-un browser. Deschideți-l (și bifați căsuța "a trecut" în colțul din dreapta sus) și ar trebui să vedeți ceva de genul:

Acest lucru ne arată că toate încercările pentru proba de proiect trec. Odată ce ați ajuns prin acest tutorial, vă recomand să deschideți spec / PlayerSpec.js fișier și peruse acest cod. Dar acum, hai să încercăm acest test.

  • Crea convert.js în src pliant.
  • Crea convertSpec.js în spec pliant,
  • Copiați SpecRunner.html fișier și redenumiți-l SpecRunner.original.html.
  • Eliminați legăturile la fișierele de proiect din eșantion SpecRunner.html și adăugați următoarele linii:

Acum suntem gata să creăm o mini-bibliotecă care va converti între unitățile de măsură. Vom începe prin a scrie testele pentru mini-biblioteca noastră.


Pasul 3: Scrierea testelor

Deci, să scriem testele noastre, să o facem?

descrie ("Convert library", functie () descrie ("converter distance", function () );

Începem cu asta; ne testează Convertit bibliotecă. Veți observa că suntem cuibărit descrie declarații aici. Acest lucru este perfect legal. Este de fapt o modalitate excelentă de a testa bucăți separate de funcționalitate ale aceleiași baze de date. În loc de două separat descrie solicită Convertit conversiile la distanță ale bibliotecii și conversiile de volum, putem avea o suită mai descriptivă de teste ca aceasta.

Acum, pe testele reale. Voi repeta interiorul descrie solicită aici pentru confortul dvs..

descrie ("convertor de distanțe", funcție () it ("convertește inci la centimetri", funcție () așteptați (Convertește (12, "in") la. ("converteste centimetri in metri", function () expect (Convert (2000, "cm") la ("yards")).

Iată testele noastre pentru conversiile la distanță. Este important să observăm ceva aici: nu am scris un cod de cod pentru noi Convertit bibliotecă, astfel că în aceste teste facem mai mult decât să verificăm dacă funcționează: de fapt decidem cum va fi folosit (și, prin urmare, pus în aplicare). Iată cum am decis să facem conversiile:

Convertit(, ).la();

Da, iau un semn de la modul în care Jasmine și-a implementat testele, dar cred că este un format frumos. Deci, în aceste două teste am făcut conversia eu (bine, cu un calculator) pentru a vedea care ar trebui să fie rezultatele apelurilor noastre. Folosim la egal Matcher pentru a vedea dacă testele noastre trec.

Iată testele de volum:

descrie ("convertorul de volum", funcția () it ("convertește litri în galoane", funcția () așteptați (Convert (3, "litri" ("converteste galonii in cani", functie () asteapta (Converti (2, "galoni") la ("cani").

Și voi adăuga încă două teste în topul nostru descrie apel:

ea ("aruncă o eroare atunci când a trecut o necunoscută din unitate", funcția () var testFn = funcția () Convert (1, "dolar") la ("yens" nouă eroare ("nerecunoscut din unitate"));); el ("aruncă o eroare atunci când a trecut o necunoscută la unitate", funcția () var testFn = funcția () Convert (1, "cm") la (furlongs); așteptați (testFn) .toThrow noua eroare ("nerecunoscut la unitate")););

Acestea verifică dacă erorile ar trebui să fie aruncate atunci când unitățile necunoscute sunt transmise fie în Convertit sau funcția la metodă. Veți observa că împachetez conversia reală într-o funcție și trec la aceasta aştepta funcţie. Asta pentru că nu putem numi funcția ca aştepta parametru; trebuie să-i dăm o funcție și să-i lăsăm să numească funcția însăși. Deoarece trebuie să trecem un parametru la asta la putem face acest lucru.

Celălalt lucru pe care trebuie să-l observăm este că introduc un nou instrument de comparare: a arunca, care ia un obiect de eroare. În curând vom examina mai multe persoane.

Acum, dacă te deschizi SpecRunner.html într-un browser, veți obține acest lucru:

Grozav! Testele noastre nu reușesc. Acum, hai să ne deschidem convert.js fișier și de a face ceva de lucru:

funcția Conversie (număr, dinUnit) var conversions = distanță: metri: 1, cm: 0,01, picioare: 0,3048, inchi: 0,0254, metri: 0,9144, volum: litri: 1, galoane: 3.785411784, cupe: 0.236588236 , întreUnit = false, tip, unitate; pentru (introduceți conversii) if (conversii (type)) if ((unitate = conversii [type] [fromUnit])) betweenUnit = number * unit * 1000;  return to: function (toUnit) if (intreUnit) pentru (introduceti conversiile) if (conversions.hasOwnProperty (type) fix (întreUnit / (unitate * 1000));  arunca o eroare nouă ("nerecunoscut la unitate");  altceva aruncați o nouă eroare ("unrecognized from-unit");  funcția fix (num) return parseFloat (num.toFixed (2)); ; 

Nu o să discutăm cu adevărat, pentru că învățăm pe Jasmine aici. Dar aici sunt punctele principale:

  • Facem conversiile stocând conversia într-un obiect; numerele de conversie sunt clasificate după tip (distanță, volum, adăugați-vă propriul). Pentru fiecare câmp de măsurare, avem o valoare de bază (de metri sau de litri, aici) pe care se convertește totul. Atunci când vezi metri: 0,9144, știi că sunt la câți metri pe metru. Apoi, pentru a transforma șantierele, spre exemplu, centimetri, se înmulțește yards de primul parametru (pentru a obține numărul de metri) și apoi împărțiți produsul cu cm, numărul de metri într-un centimetru. În acest fel, nu trebuie să stocăm ratele de conversie pentru fiecare pereche de valori. De asemenea, este ușor să adăugați noi valori mai târziu.
  • În cazul nostru, așteptăm ca unitățile introduse să fie identice cu tastele pe care le folosim în tabelul de conversie. Dacă ar fi o bibliotecă reală, am dori să acceptăm mai multe formate - cum ar fi 'in', 'inch' și 'inches' - și, prin urmare, am dori să adăugăm o anumită logică pentru a se potrivi cu fromUnit la cheia corectă.
  • La sfârșitul Convertit funcția, stocăm valoarea intermediară în betweenUnit, care este inițializată la fals. În acest fel, dacă nu avem fromUnit, betweenUnit va fi fals intrarea în la metoda, și astfel o eroare ar fi aruncată.
  • Dacă nu avem toUnit, o eroare diferită va fi aruncată. În caz contrar, vom fi împărțite ca necesare și vom returna valoarea convertită.

Acum, du-te înapoi SpecRunner.html și reîncărcați pagina. Ar trebui să vedeți acum acest lucru (după ce ați verificat? Show passed?):

Acolo te duci! Testele noastre trec. Dacă am fi dezvoltat un proiect real aici, am fi scris teste pentru o anumită bucată de funcționalitate, le vom trece, vom scrie teste pentru un alt cec, le vom face paste etc. Dar, deoarece acesta a fost un exemplu simplu, tocmai am făcut-o toate într-o singură lovitură.

Și acum că ați văzut acest exemplu simplu de a folosi Jasmine, să ne uităm la câteva caracteristici pe care le oferă.


Pasul 4: Învățarea matricilor

Până acum, am folosit două persoane: la egal și a arunca. Există, desigur, multe altele. Iată câteva dintre ele pe care le veți găsi probabil utile; puteți vedea întreaga listă de pe wiki.

toBeDefined / toBeUndefined

Dacă doriți doar să vă asigurați că o variabilă sau o proprietate este definită, există o potrivire pentru asta. Există, de asemenea, una pentru a confirma că o variabilă sau o proprietate este nedefinit.

("este definit", funcția () var name = "Andrew"; așteptați (nume) .toBeDefined ();) (););

toBeTruthy / toBeFalsy

Dacă ceva ar fi adevărat sau fals, acești oameni vor face acest lucru.

ea ("este adevărată", funcția () așteptați (Lib.isAWeekDay ()) toBeTruthy ();); aceasta ("este falsă", funcția () așteptați (Lib.finishedQuiz) .toBeFalsy (););

toBeLessThan / toBeGreaterThan

Pentru tot ce număr oameni. Știți cum funcționează acestea:

("este mai mică de 10", funcția () așteptați (5) .toBeLessThan (10);); ("este mai mare de 10", funcția () așteptați (20) .toBeGreaterThan (10););

a se potrivi

Aveți un text de ieșire care să corespundă unei expresii regulate? a se potrivi matcher este gata și dispus.

("outputs the right text", funcția () așteptați (cart.total ()) toMatch (/ \ $ \ d *. \ d \ d /);

a conține

Aceasta este destul de utilă. Verifică dacă un șir sau șir conține un element sau un subrevers.

ea ("ar trebui să conțină portocale", funcția () așteptați (["mere", "portocale", "pere")).

Există și alți câțiva prieteni care se găsesc în wiki. Dar ce faci dacă vreți ca un corespondent să nu existe? Într-adevăr, ar trebui să fii capabil să faci aproape orice cu un anumit cod de set-up și asigurați-vă de cei de la Jasmine, dar uneori este mai bine să abrogeți o parte din această logică pentru a avea un test mai ușor de citit. Serendipitously (bine, de fapt nu), Jasmine ne permite să ne creăm propriii matrici. Dar pentru a face asta, va trebui să învățăm mai întâi altceva.

Pasul 5: Acoperirea înainte și după

Adesea - atunci când testați o bază de cod - veți dori să efectuați câteva rânduri de set-up pentru fiecare test dintr-o serie. Ar fi dureros și amuzant să trebuiască să copiați asta pentru fiecare aceasta apel, astfel încât Jasmine are o caracteristică puțin utilă care ne permite să desemnați codul care să fie rulat înainte sau după fiecare încercare. Să vedem cum funcționează acest lucru:

descrie ("MyObject", funcția () var obj = new MyObject (); înainteEach (funcția () obj.setState ("curat"); ("dirty"), așteptați (obj.getState ()) toEqual ("dirty");) ("add states", function () obj.addState )). toEqual (["curat", "împachetat"]);));

În acest exemplu controversat, puteți vedea cum, înainte de fiecare test, starea obj este setat să "curăță". Dacă nu am făcut acest lucru, modificările aduse unui obiect dintr-un test anterior persistă în mod implicit la următorul test. Desigur, am putea face și ceva similar cu După fiecare funcţie:

descrie ("MyObject", functie () var obj = new MyObject ("curat"); // seta starea initiala dupaEach (function () obj.setState (" , funcția () obj.setState ("dirty"); așteptați (obj.getState ()) toEqual ("dirty" ); așteptați (obj.getState ()). toEqual (["curat", "ambalat"]);));

Aici, vom stabili obiectul pentru început și apoi vom corecta după fiecare test. Dacă doriți MyObject astfel încât să puteți încerca acest cod, îl puteți obține aici într-o listă GitHub.

Pasul 6: Scrierea personalizatelor

După cum am spus mai devreme, clienții de afaceri ar fi probabil de ajutor uneori. Deci, să scriem una. Putem adăuga o potrivire în fie a BeforeEach sau un apel aceasta (bine, bănuiesc că tu ar putea faceți-o într-un După fiecare apel, dar asta nu ar avea prea mult sens). Iată cum începeți:

înainteEach (funcția () this.addMatchers (););

Destul de simplu, nu? Noi sunam this.addMatchers, trecând un parametru obiect. Fiecare cheie din acest obiect va deveni un nume de ordonator, iar funcția asociată (valoarea) va fi modul în care este rulat. Să presupunem că vrem să creăm o potrivire care să verifice dacă un număr se află între alte două. Iată ce ați scrie:

înainteEach (funcția () this.addMatchers (toBeBetween: funcție (rangeFloor, rangeCeiling) if (rangeFloor> rangeCeiling) var temp = rangeFloor; rangeFloor = rangeCeiling; rangeCeiling = temp; return this.actual> rangeFloor && this. real < rangeCeiling;  ); );

Pur și simplu luăm doi parametri, asigurați-vă că primul este mai mic decât cel de-al doilea și returnați o afirmație booleană care se evaluează la adevărat dacă condițiile noastre sunt îndeplinite. Cel mai important lucru pe care trebuie să-l observăm aici este modul în care obținem o valoare a valorii care a fost transferată aştepta funcţie: this.actual.

("este între 5 și 30", funcția () așteptați (10). toBeBetween (5, 30);); ("este între 30 și 500", funcția () așteptați (100). toBeBetween (500, 30););

Aceasta este ceea ce SpecHelper.js fișierul nu; are o beforeEach apel care adaugă modulul de contorizare tobePlaying (). Verifică!


Concluzie: Să te distrezi!

Există mult mai multe lucruri pe care le puteți face cu Jasmine: corecții legate de funcții, spioni, specificații asincrone și multe altele. Vă recomandăm să explorați wiki-ul dacă sunteți interesat. Există, de asemenea, câteva biblioteci însoțitoare care fac mai ușor testarea în DOM: Jasmine-jQuery și Jasmine-fix (care depind de Jasmine-jQuery).

Deci, dacă nu vă testați JavaScript până acum, acum este un moment excelent pentru a începe. După cum am văzut, sintaxa rapidă și simplă a Jasmine face testarea destul de simplă. Nu ai nici un motiv să nu faci asta, acum, este acolo?

Dacă doriți să continuați dezvoltarea JavaScript, de ce nu verificați gama de articole JavaScript pe Envato Market? Există mii de scripturi, aplicații și fragmente de cod pentru a vă ajuta.

Cod