de Parth Modi

Cum să vă uscați testele RSpec folosind exemple partajate

Cum sa va uscati testele RSpec folosind exemple partajate

Acordați-mi șase ore pentru a tăia un copac și voi petrece primele patru ascuțind toporul. ” – Abraham Lincoln

Când am refactorizat un proiect în urmă cu câteva săptămâni, mi-am petrecut cea mai mare parte a timpului scriind specificații. După ce am scris câteva cazuri de testare similare pentru unele API-uri, am început să mă întreb dacă aș putea să scap de o mulțime de această duplicare.

Așa că m-am aruncat să citesc cele mai bune practici pentru testele de uscare (Nu te repeta). Și așa am ajuns să știu shared examples și shared contexts.

În cazul meu, am ajuns să folosesc exemple comune. Iată ce am învățat până acum de la aplicarea acestora.

Când aveți mai multe specificații care descriu un comportament similar, ar putea fi mai bine să extrageți exemple redundante în shared examples și utilizați-le în mai multe specificații.

Să presupunem că aveți două modele Utilizator și Post, iar un utilizator poate avea multe postări. Utilizatorii ar trebui să poată vizualiza lista de utilizatori și postări. Crearea unei acțiuni index în utilizatorii și controlerele de postări va servi acestui scop.

Mai întâi, scrieți specificații pentru acțiunea dvs. de index pentru controlerul utilizatorilor. Va avea responsabilitatea de a prelua utilizatorii și de a-i reda cu un aspect adecvat. Apoi scrieți suficient cod pentru a trece testele.

# users_controller_spec.rbdescribe "GET #index" do  before do     5.times do      FactoryGirl.create(:user)     end    get :index  end  it {  expect(subject).to respond_with(:ok) }  it {  expect(subject).to render_template(:index) }  it {  expect(assigns(:users)).to match(User.all) }end
# users_controller.rbclass UsersController < ApplicationController  ....  def index    @users = User.all  end  ....end

De obicei, acțiunea index a oricărui controler preluează și agregă date din puține resurse, după cum este necesar. De asemenea, adaugă paginarea, căutarea, sortarea, filtrarea și extinderea.

În cele din urmă, toate aceste date sunt prezentate vizualizărilor prin HTML, JSON sau XML folosind API-uri. Pentru a simplifica exemplul meu, acțiunile de index ale controlerelor vor prelua datele, apoi le vor afișa prin vizualizări.

Același lucru este valabil și pentru acțiunea index din controlerul de postări:

describe "GET #index" do   before do     5.times do      FactoryGirl.create(:post)    end    get :index  end  it {  expect(subject).to respond_with(:ok) }  it {  expect(subject).to render_template(:index) }  it {  expect(assigns(:posts)).to match(Post.all) }end
# posts_controller.rbclass PostsController < ApplicationController  ....  def index    @posts = Post.all  end  ....end

Testele RSpec scrise atât pentru utilizatori, cât și pentru controlerul de postări sunt foarte similare. În ambele controlere avem:

  • Codul de răspuns – ar trebui să fie „OK”
  • Ambele acțiuni index ar trebui să redea parțial sau vizualizare corespunzătoare – în cazul nostru index
  • Datele pe care dorim să le redăm, cum ar fi postările sau utilizatorii

Să uscăm specificațiile pentru acțiunea noastră index folosind shared examples.

Unde să-ți pui exemplele comune

Îmi place să plasez exemple comune în interiorul specs / support / shared_examples director, astfel încât toate shared example-fișierele legate sunt încărcate automat.

Puteți citi despre alte convenții frecvent utilizate pentru localizarea dvs. shared examples Aici: documente de exemple comune

Cum se definește un exemplu comun

Acțiunea dvs. de indexare trebuie să răspundă cu 200 de coduri de succes (OK) și să redea șablonul de indexare.

RSpec.shared_examples "index examples" do   it { expect(subject).to respond_with(:ok) }  it { expect(subject).to render_template(:index) }end

În afară de it blocuri – și înainte și după cârlige – puteți adăuga let blocuri, context și descrie blocuri, care pot fi definite și în interior shared examples.

Personal, prefer să păstrez exemple simple și concise, și nu adaug contexte și nu las blocuri. shared examples block acceptă, de asemenea, parametrii, pe care îi voi acoperi mai jos.

Cum se utilizează exemple comune

Se adaugă include_examples "index examples" specificațiilor utilizatorului și a controlerului de postări include „exemple de index” la testele dvs.

# users_controller_spec.rbdescribe "GET #index" do  before do     5.times do      FactoryGirl.create(:user)     end    get :index  end  include_examples "index examples"  it {  expect(assigns(:users)).to match(User.all) }end
# similarly, in posts_controller_spec.rbdescribe "GET #index" do  before do     5.times do      FactoryGirl.create(:post)     end    get :index  end  include_examples "index examples"  it {  expect(assigns(:posts)).to match(Post.all) }end

Puteți utiliza, de asemenea it_behaves_like sau it_should_behaves_like în loc de include_examples în acest caz. it_behaves_like și it_should_behaves_like sunt de fapt pseudonime și funcționează în același mod, astfel încât să poată fi utilizate în mod interschimbabil. Dar include_examples și it_behaves_like sunt diferite.

După cum se menționează în documentația oficială:

  • include_examples – include exemple în contextul actual
  • it_behaves_like și it_should_behave_like include exemplele într-un context imbricat

De ce contează această distincție?

Documentația RSpec oferă un răspuns adecvat:

Când includeți exemple parametrizate în contextul curent de mai multe ori, puteți suprascrie definițiile metodei anterioare și ultimele câștiguri ale declarației.

Deci, atunci când vă confruntați cu situații în care exemplele parametrizate conțin metode care intră în conflict cu alte metode în același context, puteți înlocui include_examples cu it_behaves_like metodă. Acest lucru va crea un context imbricat și va evita acest tip de situații.

Consultați următoarea linie din specificațiile controlerului utilizatorilor și postează specificațiile controlerului:

it { expect(assigns(:users)).to match(User.all) }it { expect(assigns(:posts)).to match(Post.all) }

Acum, specificațiile controlerului dvs. pot fi re-luate în considerare, trecând parametrii la exemplul partajat, după cum urmează:

# specs/support/shared_examples/index_examples.rb
# here assigned_resource and resource class are parameters passed to index examples block RSpec.shared_examples "index examples" do |assigned_resource, resource_class|   it { expect(subject).to respond_with(:ok) }  it { expect(subject).to render_template(:index) }  it {  expect(assigns(assigned_resource)).to match(resource_class.all)   }end

Acum, efectuați următoarele modificări ale specificațiilor utilizatorului și ale controlerului de postări:

# users_controller_spec.rbdescribe "GET #index" do  before do     ...  end  include_examples "index examples", :users, User.allend
# posts_controller_spec.rbdescribe "GET #index" do  before do     ...  end  include_examples "index examples", :posts, Post.allend

Acum, specificațiile controlerului arată curat, mai puțin redundant și mai important, DRY. În plus, aceste exemple de index pot servi ca structuri de bază pentru proiectarea acțiunii de index a altor controlere.

Concluzie

Prin mutarea exemplelor obișnuite într-un fișier separat, puteți elimina duplicarea și îmbunătăți coerența acțiunilor controlerului în întreaga aplicație. Acest lucru este foarte util în cazul proiectării API-urilor, deoarece puteți utiliza structura existentă a testelor RSpec pentru a proiecta teste și a crea API-uri care aderă la structura dvs. comună de răspuns.

În general, când lucrez cu API-uri, îl folosesc shared examples pentru a-mi oferi o structură comună pentru a proiecta API-uri similare.

Simțiți-vă liber să împărtășiți modul în care vă uscați specificațiile folosind shared examples.