Ruby pentru începători Testarea cu Rspec

Ruby este una dintre cele mai populare limbi folosite pe web. Desfășurăm o sesiune aici pe Nettuts +, care vă va prezenta Ruby, precum și marile cadre și instrumente care merg împreună cu dezvoltarea Ruby. În acest episod, veți afla despre testarea codului dvs. Ruby cu Rspec, una dintre cele mai bune biblioteci de testare din domeniu.


Preferați un scenariu?

Arată cunoscut?

Dacă ați citit tutorialul meu recent despre JasmineJS, probabil veți observa mai multe asemănări în Rspec. De fapt, asemănările sunt în Jasmine: Jasmine a fost creată cu Rspec în minte. Vom analiza cum să putem folosi Rspec pentru a face TDD în Ruby. În acest tutorial, vom crea câteva cursuri Ruby făcute pentru a ne familiariza cu sintaxa Rspec. Cu toate acestea, următoarea? Ruby pentru Newbies? episodul se va utiliza utilizând Rspec împreună cu alte biblioteci pentru a testa aplicațiile web? deci stați bine!


Configurare

Este destul de ușor să instalați Rspec. Pop deschideți acea linie de comandă și executați aceasta:

gem install rspec

Asa de usor.

Acum, să organizăm un mic proiect. Vom crea două clase: Carte și Bibliotecă. Al nostru Carte obiectele vor stoca doar un titlu, un autor și o categorie. Al nostru Bibliotecă Obiectul va stoca o listă de cărți, le va salva într-un fișier și ne va permite să le preluăm după categorie.

Iată ce ar trebui să arate directorul dvs. de proiect:

Am introdus specificațiile (sau specificațiile) într-un spec pliant; avem un fișier spec pentru fiecare clasă. Observați spec_helper.rb fişier. Pentru ca specificațiile noastre să fie difuzate, trebuie să facem asta necesita clasele Ruby pe care le testăm. Asta facem noi înăuntru spec_helper fişier:

require_relative '? / biblioteca 'require_relative'? / carte "necesită" yaml "

(Te-ai întâlnit require_relative inca? Nu? Bine, require_relative este la fel ca necesita, cu excepția faptului că în loc să caute calea ta Ruby, ea caută rudă în directorul curent.)

Este posibil să nu fiți familiarizat cu modulul YAML; YAML este o bază de date simplă pe care o vom folosi pentru a stoca date. Veți vedea cum funcționează și vom vorbi mai târziu despre aceasta.

Deci, acum că suntem cu toții pregătiți, hai să ne spargem câteva specificații!


Carte Clasă

Să începem cu testele pentru Carte clasă.

cereți "spec_helper" descrie Cartea se termină

Așa începem: cu a descrie bloc. Parametrul nostru la descrie explică ceea ce testează: ar putea fi un șir, dar în cazul nostru folosim numele clasei.

Deci, ce vom pune înăuntru descrie bloc?

înainte: fiecare facebook = Book.new "Titlu", "Autor",: sfârșitul categoriei

Vom începe prin apelarea inainte de; trecem simbolul :fiecare pentru a specifica că vrem ca acest cod să fie rulat înainte de fiecare test (am putea face și el :toate să o executați o dată înainte de toate testele). Ce anume facem înainte de fiecare test? Creăm o instanță Carte. Observați cum facem o variabilă de instanță, prefixând numele variabilei cu @. Trebuie să facem acest lucru pentru ca variabila noastră să fie accesibilă din cadrul testelor noastre. În caz contrar, vom obține o variabilă locală care este bună numai în interiorul inainte de bloc? care nu este deloc bună.

Trecând peste,

descrie "#new" face "ia trei parametri și returnează un obiect Carte" face @ book.should be_an_instance_of sfârșitul cărții

Iată primul nostru test. Folosim un imbricat descrie blocați aici pentru a spune că descriem acțiunile unei anumite metode. Veți observa că am folosit șirul? #New ?; este o convenție în Ruby de a vorbi referiți-vă la metodele instanței de genul: ClassName # METHODNAME Deoarece avem numele clasei în topul nostru descrie, tocmai punem numele metodei aici.

Testul nostru doar confimă faptul că suntem într-adevăr un obiect Carte.

Observați gramatica pe care o folosim aici: object.should do_something. Nouăzeci și nouă de procente din testele dvs. vor lua această formă: aveți un obiect și începeți să sunați ar trebui să sau nu ar trebui pe obiect. Apoi, treceți la acest obiect chemarea la o altă funcție. În acest caz, asta e be_an_instance_of (care ia Carte ca parametru unic). În total, acest lucru face un test perfect lizibil. Este foarte clar că @carte ar trebui să fie o instanță a clasei Carte. Deci, să o conducem.

Deschideți terminalul, CD în directorul de proiect și executați rspec spec. spec este dosarul în care se află rspec vor găsi testele. Ar trebui să vedeți ieșirea spunând ceva despre obiectul constant neinitializat :: Rezervați ?; asta înseamnă că nu există Carte clasă. Să rezolvăm asta.

Potrivit TDD, dorim doar să scriem un cod suficient pentru a rezolva această problemă. În book.rb dosar, ar fi aceasta:

clasa de sfârșit de carte

Rulați din nou testul (rspec spec), și veți găsi că trece bine. Nu avem inițializa metodă, deci chemare Ruby # nou nu are efect acum. Dar, putem crea Carte obiecte (deși goale). În mod normal, am urmări acest proces prin restul dezvoltării noastre: scrieți un test (sau câteva teste conexe), urmăriți-l că nu reușiți, treceți, repetați, repetați. Cu toate acestea, pentru acest tutorial, vă voi arăta doar testele și codul și le vom discuta.

Deci, mai multe teste pentru Carte:

descrieți "#title" faceți-o "returnează titlul corect" nu @ book.title.should eql "Titlu" sfârșitul final descrie "#author" face "returnează autorul corect" nu @ book.author.should eql " sfârșitul final descrie "#category" face "returnează categoria corectă" do @ book.category.should eql: category end end

Ar trebui să vă fie destul de îndrăznețe. Dar observați cum comparăm în test: cu EQL. Există trei modalități de testare a egalității cu Rspec: utilizarea operatorului == sau metoda EQL ambele întoarceți Adevărat dacă cele două obiecte au același conținut. De exemplu, ambele sunt șiruri sau simboluri care spun același lucru. Apoi este egal, care returneaza numai adevarul in cele doua obiecte sunt intr-adevar si intr-adevar egale, adica ele sunt acelasi obiect in memorie. În cazul nostru, EQL (sau ==) este ceea ce vrem.

Acestea vor eșua, deci iată codul Carte pentru a le face să treacă:

clasa Cărți attr_accessor: titlu,: autor,: categorie def initialize titlu, autor, categorie @title = title @author = author @category = sfârșitul categoriei

Să mergem mai departe Bibliotecă!


Specând afară Bibliotecă clasă

Acesta va fi un pic mai complicat. Să începem cu acest lucru:

cereți "spec_helper" să descrieți "Obiect de bibliotecă" înainte: toate au lib_obj = [Book.new ("JavaScript: The Good Parts", "Douglas Crockford" Jeffrey Zeldman ",: design), Book.new (" Nu mă face să mă gândesc "," Steve Krug ",: usability), Book.new (" Modele JavaScript "," Stoyan Stefanov "). nou ("Design Web Responsabil", "Ethan Marcotte",: design)] File.open "books.yml", "w" nu | f.write YAML :: dump lib_obj sfârșitul final înainte: fiecare face @lib = Library.new "books.yml" end end

Toate acestea sunt setate: folosim două inainte de blocuri: una pentru :fiecare și unul pentru :toate. În înainte: toate bloc, creăm o serie de cărți. Apoi deschidem fișierul? Books.yml? (în modul de funcționare) și de utilizare YAML pentru a arunca matricea în fișier.

Traseu scurt de iepure pentru a explica YAML un pic mai bine: YAML este, conform site-ului, un standard uman de serializare a datelor pentru toate limbile de programare. Este ca o bază de date bazată pe text, cam ca JSON. Importăm YAML în noi spec_helper.rb. YAML modulul are două metode principale pe care le veți utiliza: depozit, care transmite datele serializate ca șir. Atunci, sarcină ia șirul de date și o ascunde în obiecte Ruby.

Deci, am creat acest fișier cu unele date. Inainte de :fiecare test, vom crea o Bibliotecă obiect, trecând numele fișierului YAML. Acum, să vedem testele:

descrieți "#new" face contextul "fără parametri" face "nu are cărți" nu lib lib.should have (0) .books end end context "cu un parametru fișier yaml" a face "are cinci cărți "do @ lib.există (5) .books sfârșitul sfârșitul sfârșitul aceasta" returnează toate cărțile într-o anumită categorie "nu @ lib.get_books_in_category (: development) .length.should == 2 sfârșitul" acceptă cărți noi "nu @ lib.add_book (Book.new ("Proiectare pentru Web", "Mark Boulton",: design)) @ lib.get_book ("Proiectare pentru Web") ar trebui să fie_in_instance_of sfârșitul cărții "salvează biblioteca" = @ lib.books.map | carte | book.title @ lib.save lib2 = Bibliotecă.new "books.yml" books2 = lib2.books.map | carte | book.title books.should eql books2 end

Începem cu un interior descrie blocați în special pentru Biblioteca # nou metodă. Introducem un alt bloc aici: context Acest lucru ne permite să specificăm un context pentru testele din interiorul acestuia sau să specificăm diferite rezultate pentru situații dificile. În exemplul nostru, avem două contexte diferite:? Fără parametri? și? cu un parametru de fișier yaml ?; acestea arată cele două comportamente de utilizare Biblioteca # nou.

De asemenea, observați materialele de testare pe care le folosim în aceste două teste: lib.există (0) .books și @ lib.există (5) .books. Cealaltă modalitate de a scrie acest lucru ar fi lib.books.length.should == 5, dar acest lucru nu este la fel de lizibil. Cu toate acestea, arată că trebuie să avem a cărți proprietate care este o serie de cărți pe care le avem.

Apoi, avem alte trei teste pentru a testa funcționalitatea de a obține cărți pe categorii, a adăuga o carte în bibliotecă și a salva biblioteca. Toate acestea nu funcționează, așa că acum să scriem clasa.

clasa de bibliotecă attr_accessor: cărți def initialize lib_file = false @lib_file = lib_file @books = @lib_file? YAML :: încărcare (File.read (@lib_file)): [] end def get_books_in_category categorie @ books.select do | book | book.category == sfarsit categorie def add_book book @ books.push end book def get_book title @ books.select do | carte | book.title == titlu end.first end def salva lib_file = false @lib_file = lib_file || @lib_file || "library.yml" File.open @lib_file, "w" nu | f | f.write YAML :: dump @books end end end

Am putea scrie mai multe teste și adăugăm o mulțime de alte funcționalități în acest sens Bibliotecă dar ne oprim acolo. Acum alergând rspec spec, veți vedea că toate încercările trec.

Acest lucru nu ne oferă multe informații despre teste. Dacă doriți să vedeți mai multe, utilizați parametrul de format imbricat: rspec spec - format imbricat. Veți vedea acest lucru:


Câțiva ultimii maeștri

Înainte să încheiem, permiteți-mi să vă arăt câteva cupluri

  • obj.should be_true, obj.should be_false, obj.should be_nil, obj.should be_empty - primele trei dintre acestea ar putea fi realizate de către == true, etc. be_empty va fi adevărat dacă obj.empty? este adevarat.
  • ar trebui să existe - acest obiect există încă?
  • obj.should have_at_most (n) .items, object.should_at_least (n) .items - ca avea, dar va trece dacă există mai mult sau mai puțin decât n articole, respectiv.
  • ar trebui să includă (a [, b ,?]) - sunt unul sau mai multe elemente într-un matrice?
  • obj.should se potrivesc (string_or_regex) - obiectul se potrivește cu șirul sau cu regex?
  • obj.should raise_exception (eroare) - această metodă generează o eroare atunci când este apelată?
  • obj.should răspuns_to (nume_metodă) - acest obiect are această metodă? Poate să ia mai mult de un nume de metodă, fie în șiruri sau simboluri.

Doriți să aflați mai multe?

Rspec este unul dintre cele mai bune cadre pentru testare în Ruby, și există o tona pe care o poți face cu ea. Pentru a afla mai multe, consultați site-ul Rspec. Există și cartea The Rspec, care învață mai mult decât Rspec: este vorba despre TDD și BDD în Ruby. Acum o citesc, și este extrem de profundă și profundă.

Asta e totul pentru această lecție! Data viitoare vom analiza modul în care putem utiliza Rspec pentru a testa interfețele într-o aplicație web.

Cod