Cum încerc

Într-o discuție recentă despre Google+, un prieten de-al meu a comentat:Dezvoltarea bazată pe dezvoltare (TDD) și dezvoltarea bazată pe comportament (BDD) este Ivory Tower BS."Acest lucru ma determinat sa ma gandesc la primul meu proiect, cum am simtit acelasi lucru atunci si cum ma simt acum. De la primul proiect, am dezvoltat un ritm de TDD / BDD care nu numai ca functioneaza pentru mine, ci si pentru mine. pentru client, de asemenea.

Ruby on Rails navează cu o suită de test, numită Unitate de testare, dar mulți dezvoltatori preferă să utilizeze RSpec, Castraveți sau o combinație a celor două. Personal, prefer acest lucru, folosind o combinație a celor două.


RSpec

De pe site-ul RSPE:

RSPE este un instrument de testare pentru limbajul de programare Ruby. Născut sub bannerul dezvoltării bazate pe comportament, este conceput pentru a face ca dezvoltarea bazată pe test să fie o experiență productivă și plăcută.

RSpec oferă un DSL puternic, care este util atât pentru testarea unității, cât și pentru integrarea. În timp ce am folosit RSpec pentru scrierea testelor de integrare, prefer să o folosesc doar într-o capacitate de testare a unității. Prin urmare, voi studia modul în care folosesc RSpec exclusiv pentru testarea unitară. Vă recomand să citiți Cartea RSpec de David Chelimsky și alții pentru o acoperire completă și în profunzime a RSpec.


Castravete

Am constatat că beneficiile TDD / BDD depășesc cu mult contra.

Cucumber este un framework de testare pentru integrare și acceptare care suportă Ruby, Java, .NET, Flex și o serie de alte limbi și cadre de web. Puterea sa adevărată provine din DSL-ul său; nu numai că este disponibil în limba engleză, dar a fost tradusă în peste patruzeci de limbi vorbite.

Cu un test de acceptare citit de om, puteți să îl semnalizați pe un utilizator înainte de a scrie o singură linie de cod. Ca și în cazul RSpec, voi acoperi numai Castraveți în calitatea în care o folosesc. Pentru o analiză completă despre Castraveți, citiți Cartea Castravetei.


Pregatirea

Să începem mai întâi un nou proiect, instruindu-l pe Rails să renunțe la Unitatea de testare. Introduceți următoarele într-un terminal:

 șine noi cum_i_test -T

În cadrul Gemfile, adăuga:

 sursă "https: //rubygems.org" ... grup: test gem "capybara" gem "castraveți-șine", necesită: gem fals "baza de date curat" gem "factory_girl_rails 'gem' shoulda ' gem "rspec-șine" sfârșitul

Eu folosesc cea mai mare parte RSpec pentru a se asigura că modelele mele și metodele lor rămân în control.

Aici, am pus Cucumber și prieteni în interiorul grupului Test bloc. Acest lucru asigură că acestea sunt încărcate corect numai în mediul de testare Rails. Observați modul în care încărcăm, de asemenea, RSPE în interiorul dezvoltare și Test blocuri, făcându-l disponibil în ambele medii. Există și alte câteva pietre prețioase. pe care o voi detalia în cele ce urmează. Nu uita să fugi instalare pachet pentru a le instala.

  • Capibara: simulează interacțiunile browserului.
  • Bază de date Cleaner: curata baza de date intre runurile de test.
  • Factory Girl Rails: înlocuirea dispozitivului de fixare.
  • Trebuia să: metode și metode de ajutor pentru RSpec.

Trebuie să executăm aceste generatoare de pietre pentru a le instala. Puteți face acest lucru cu următoarele comenzi terminale:

 rails g rspec: instalare creare .rspec creare spec. creare spec / spec_helper.rb rails g castravete: instalare creare config / cucumber.yml creare script / castravete chmod script / castron creare caracteristici / step_definitions creare caracteristici / suport creare caracteristici / support / env. rb exist lib / sarcini creare lib / tasks / cucumber.rake gsub config / database.yml gsub config / database.yml forță config / database.yml

În acest moment, am putea începe să scriem specificații și culegeri pentru a testa cererea noastră, dar putem stabili câteva lucruri pentru a face mai ușoară testarea. Să începem în application.rb fişier.

 Modul de utilizare a clasei HowITest < Rails::Application config.generators do |g| g.view_specs false g.helper_specs false g.test_framework :rspec, :fixture => adevărat g.fixture_replacement: factory_girl,: dir => 'spec / fabrici' end ... end end

În cadrul clasei Application, depășim câteva dintre generatoarele implicite ale Rails. Pentru primele două, depășim specificațiile privind generațiile de vizionări și ajutoare.

Aceste teste nu sunt necesare, deoarece folosim doar RSpec pentru teste unitare.

Cea de-a treia linie informează Rails că intenționăm să utilizăm RSPE ca cadru de testare al nostru și ar trebui să genereze și elemente de pregătire atunci când generează modele. Linia finală asigură faptul că folosim factory_girl pentru dispozitivele noastre, din care sunt create în spec / fabrici director.


Prima noastră funcție

Pentru a păstra lucrurile simple, vom scrie o caracteristică simplă pentru înscrierea în aplicația noastră. Din motive de coerență, voi trece peste implementarea reală și voi rămâne cu suita de testare. Iată conținutul Caracteristici / signing_in.feature:

 Caracteristică: Conectare Pentru a utiliza aplicația Ca utilizator înregistrat Vreau să vă conect prin intermediul unui formular Scenariu: Conectarea prin formular Având în vedere că există un utilizator înregistrat cu e-mail "[email protected]" Și eu sunt pe semn în pagina Când introduc acreditările corecte Și apăs butonul de semnare Apoi mesajul flash trebuie să fie "Semnat cu succes".

Când vom rula acest lucru în terminal cu caracteristici de castravete / signing_in.feature, vedem o mulțime de ieșire care se încheie cu pașii noștri nedefiniți:

 Având / ^ există un utilizator înregistrat cu e-mail "(. *?)" $ / Do | arg1 | în așteptare # exprimați regexp de mai sus cu codul pe care doriți să-l fi terminat dat / ^ Sunt pe pagina de sign in $ / do în așteptare # exprimați regexp de mai sus cu codul pe care doriți să-l fi terminat Când / ^ am introdus acreditările corecte $ / face în așteptare # exprimați regexp de mai sus cu codul pe care doriți să-l fi terminat Când / ^ apăsați butonul de semnare $ / do în așteptare # exprimați regexp de mai sus cu codul pe care doriți să-l terminați Apoi / ^ mesajul flash ar trebui să fie " (. *?) "$ / do | arg1 | în așteptare # exprimați regexp de mai sus cu codul pe care doriți să-l terminați

Următorul pas este să definiți ceea ce așteptăm la fiecare dintre acești pași. Exprimăm acest lucru în Caracteristici / stepdefinitions / signin_steps.rb, folosind Ruby simplu cu selectori Capybara și CSS.

 Având / ^ există un utilizator înregistrat cu e-mail "(. *?)" $ / Do | email | @user = FactoryGirl.create (: utilizator, e-mail: e-mail) sfarsit dat / ^ Sunt pe pagina de sign in $ / do vizita sign_in_path end Când / ^ introduceți acreditările corecte $ / do fillin "Email" .email fillin "Password", cu: @ user.password end Când / ^ apăs butonul de conectare $ / do click_button "Sign in" sfârșitul Apoi / ^ mesajul flash ar trebui să fie "(. *?)" $ / do | Text | în (". flash") nu pagina.would have_content capăt sfârșitul textului

În cadrul fiecăruia dintre Dat, Cand, și Atunci blocuri, folosim Capybara DSL pentru a defini ceea ce așteptăm de la fiecare bloc (cu excepția primului). În primul bloc dat, îi spunem factory_girl pentru a crea un utilizator stocat în utilizator exemplu pentru o utilizare ulterioară. Dacă alergi caracteristici de castravete / signing_in.feature din nou, ar trebui să vedeți ceva similar cu următorul:

 Scenariu: Conectarea prin formularul # features / signing_in.feature: 6 Dat fiind că există un utilizator înregistrat cu e-mail "[email protected]" # features / step_definitions / signing \ _in \ _steps.rb: ArgumentError) ./features/step_definitions/signing\_in\_steps.rb:2:in '/ ^ există un utilizator înregistrat cu e-mail "(. *?)" $ / "Features / signing_in.feature: 7: in' Given există un utilizator înregistrat cu e-mail "[email protected]"

Putem vedea din mesajul de eroare că exemplul nostru nu reușește pe linia 1 cu un ArgumentError din fabrica utilizatorului nu este înregistrată. Am putea crea aceasta fabrica noi insine, dar unele din magia pe care am creat-o mai devreme il vor face pe Rails sa faca asta pentru noi. Atunci când generăm modelul nostru de utilizatori, obținem gratuit fabrica de utilizatori.

 rails g model utilizator e-mail: string-ul parola: string invoke active_record creare db / migrate / 20121218044026 \ _create \ _users.rb crea app / models / user.rb invoca rspec creare spec / models / user_spec.rb invoke factory_girl .rb

După cum puteți vedea, generatorul de modele invocă factory_girl și creează următorul fișier:

ruby spec / fabrici / users.rb FactoryGirl.define fabrică: utilizatorul face email "MyString" parola "MyString" end end

Nu voi intra în profunzime aici, dar puteți citi mai multe în ghidul lor de început. Nu uita să fugi rake db: migrați și rake db: test: pregătiți pentru a încărca noua schemă. Acest lucru ar trebui să primească primul pas al caracteristicilor noastre pentru a trece și vă va porni pe drumul de a folosi Castraveți pentru testarea dvs. de integrare. Pe fiecare pas al caracteristicilor dvs., Castraveți vă va îndruma către piesele pe care le consideră lipsite pentru a trece.


Testarea modelului cu RSpec și Shoulda

Eu folosesc cea mai mare parte RSpec pentru a vă asigura că modelele și metodele lor rămân în control. Eu de multe ori îl folosesc și pentru unele teste controler la nivel înalt, dar acest lucru merge mai mult în detaliu decât permite acest ghid. Vom folosi același model de utilizator pe care l-am creat anterior cu funcția noastră de conectare. Privind înapoi la ieșirea de la rularea generatorului de model, putem vedea că am și noi user_spec.rb gratuit. Dacă fugim rspec spec / models / user_spec.rb ar trebui să vedem următoarea ieșire.

 În așteptare: utilizatorul adaugă câteva exemple la (sau șterge) /Users/janders/workspace/how\_i\_test/spec/models/user_spec.rb

Și dacă deschidem acel dosar, vedem:

 cereți "spechelper" descrie Utilizatorul face în așteptare "adaugă câteva exemple la (sau șterge) # FILE" sfârșit

Linia în așteptare ne oferă ieșirea pe care am văzut-o în terminal. Vom folosi ActiveRecord și ActiveModel pentru a vă asigura că modelul de utilizator corespunde logicii noastre de afaceri.

 trebuie să descrie contextul "#fields" face ar trebui să răspundă (: email) it ar trebui să răspundă (: parola) it ar trebui să răspundă (: contextul "#validations" trebuie să validate_presence_of (: email) it should validate_presence_of (: password) it should validate_uniqueness_of (: email) context end "#associations" descrieți "#methods" nu permite! (: user) FactoryGirl.create (: user) numele "trebuie să returneze numele utilizatorului" do user.name.should eql "Testy McTesterson"

Setăm câteva blocuri de context în interiorul primului nostru descrie blocați pentru a testa lucruri precum câmpuri, validări și asociații. Deși nu există diferențe funcționale între a descrie și a context bloc, este una contextuală. Folosim descrie blochează pentru a stabili starea a ceea ce testează și context blocuri pentru gruparea acestor teste. Acest lucru face ca testele noastre să fie mai ușor de citit și să fie întreținute pe termen lung.

Primul descrie ne permite să testeze împotriva Utilizator model într-o stare nemodificată.

Utilizăm această stare nemodificată pentru a testa baza de date cu grupurile de grupuri care ar trebui grupate fiecare după tip. Urmatorul descrie bloc stabilește un utilizator de la noi create anterior utilizator fabrică. Configurarea utilizatorului cu lăsa metoda din interiorul acestui bloc ne permite să testați o instanță a modelului nostru de utilizator împotriva atributelor cunoscute.

Acum, când fugim rspec spec / models / user_spec.rb, vedem că toate noile noastre teste eșuează.

 Efectele: 1) Utilizatorul # nume de metode ar trebui să returneze numele utilizatorilor Eșec / Eroare: user.name.should eql "Testy McTesterson" NoMethodError: nume metode nedefinite "pentru # # ./spec/models/user_spec.rb:26:inbloc (3 niveluri) în '2) Validări utilizator # Eșec / eroare: acesta validate_uniqueness_of (: email) Erorile așteptate pentru a include "a fost deja luată" când e-mailul este setat la "string ", nu a primit erori # ./spec/models/userspec.rb: 15: în bloc (3 niveluri) în "3) Utilizări # validări Eșec / eroare: validate_presence_of (: password) Erori așteptate pentru a include" nu poate fi gol "când parola este setată la zero, nu a primit erori # ./spec/models/user_spec.rb : 14: în bloc (3 niveluri) în "4) Utilizări # validări Eșec / eroare: acesta validate_presence_of (: email) Erori așteptate pentru a include" nu pot fi goale "când e-mailul este setat la zero, nu are erori #/spec/models/user_spec.rb : 13: în bloc (3 niveluri) în "5) Asociații ale utilizatorilor # Eșec / Eroare: acesta ar trebui să aibămany (: tasks) Utilizatorul așteptat pentru a avea omulte asociații numite sarcini (fără asociere numite sarcini) # ./spec/models/user_spec.rb:19:in 'bloc (3 nivele) în "6) Utilizarea câmpurilor # Eșec / Eroare: aceasta ar trebui să răspundăa duranume) așteptat # pentru a răspunde la: ultimulnume # ./spec/models/userspec.rb: 9: în bloc (3 nivele) în "7) Utilizarea câmpurilor # Eșec / Eroare: aceasta ar trebui să răspundăla (: mai întâinume) așteptat # pentru a răspunde: în primul rândnume # ./spec/models/userspec.rb: 8: în bloc (3 niveluri) în '

Cu fiecare dintre aceste teste eșuează, avem cadrul necesar pentru a adăuga la modelele noastre migrații, metode, asociații și validări. Pe măsură ce aplicația noastră evoluează, modelele se extind și schimbați schemele noastre, acest nivel de testare ne oferă protecție pentru introducerea schimbărilor de rupere.


Concluzie

În timp ce nu am acoperit prea multe subiecte în profunzime, acum ar trebui să aveți o înțelegere de bază a integrării și testarea unității cu Cucumber și RSpec. TDD / BDD este unul dintre lucrurile pe care dezvoltatorii fie par să le facă sau nu, dar am constatat că beneficiile TDD / BDD depășesc cu mult conurile pe mai multe ocazii.

Cod