RSpec Testovi za Rails Kontroler
Rails je okvir za veb razvoj, gde su model , prikaz i kontroler važni aspekti vaše aplikacije. Kontrolere, baš kao i modele i prikaze, treba testirati pomoću omiljenog alata Rubi zajednice, RSpec .
Kontroleri u Rails-u prihvataju HTTP zahteve kao svoj ulaz i isporučuju natrag i HTTP odgovor kao izlaz.
Organizovanje testova
Blokovi opisa i konteksta su presudni za održavanje testova organizovanih u čistu hijerarhiju, zasnovanu na akcijama kontrolera i kontekstu koji testiramo. Betterspecs.org pruža osnove pisanja testova i pomoći će vam da testove učinite mnogo izražajnijim.
Svrha „opisivanja“ je umotavanje skupa testova protiv jedne funkcionalnosti, dok je „kontekst“ umotavanje skupa testova protiv jedne funkcionalnosti u isto stanje. Describe vs. Context in RSpec od Ming Liu
Želite da kreirate kontekst za svaki značajan ulaz i da ga umotate u blok za opis.
Mi ćemo izraziti svaki HTTP sesije u različitim opisuju blokova za: stories_controller_spec.rb.
describe "Stories" do
describe "GET stories#index" do
context "when the user is an admin" do
it "should list titles of all stories"
end
context "when the user is not an admin" do
it "should list titles of users own stories" do
end
Kada želite da kontrolišete pristup autorizaciju, možete stvoriti novi kontekst za svaku korisničku ulogu. Na isti način možete da upravljate pristupom za potvrdu identiteta tako što ćete stvoriti novi kontekst za prijavljene i odjavljene korisnike.
context "when the user is logged in" do
it "should render stories#index"
end
context "when the user is logged out" do
it "should redirect to the login page"
end
end
Podrazumevano, konfiguracija RSpec-Rails onemogućava prikazivanje šablona za specifikacije kontrolera. Možete ga omogućiti dodavanjem render_views
:
1.Globalno, dodavanjem za RSpec.configure
blok u rails_helper.rb
datoteci
2.Po pojedinačnoj grupi
describe "GET stories#show" do
it "should render stories#show template" do
end
end
describe "GET stories#new" do
it "should render stories#new template" do
end
end
Veoma je često proveriti da li koristite važeće ili nevaljane atribute pre nego što ih sačuvate u bazi podataka.
describe "POST stories#create" do
context "with valid attributes" do
it "should save the new story in the database"
it "should redirect to the stories#index page"
end
context "with invalid attributes" do
it "should not save the new story in the database"
it "should render stories#new template"
end
end
end
Kako pripremiti podatke?
Koristimo fabrike da bismo pripremili podatke za specifikacije naših kontrolera. Način rada fabrika može se poboljšati pomoću FactoryBot gema.
Sa sledećom fabrikom generisaćemo više priča koristeći sequencerazličite naslove i sadržaje:
FactoryBot.define do
factory :story do
user
sequence(:title) { |n| "Title#{n}" }
sequence(:content) { |n| "Content#{n}" }
end
end
Da testiramo ovo!
Došlo je vreme da kreiramo sopstvene testove kontrolera. Testovi su napisani korišćenjem RSPEC i Capybara . Pokrićemo stories_controller.rb
testove za svaku od ovih metoda:
#index
Prvo želimo da pogledamo naš kontroler stories_controller.rb
. Akcija indeksa ovlašćuje pristup pričama u zavisnosti od toga da li je trenutni korisnik administrator:
def index
@stories = Story.view_premissions(current_user).
end
I u modelu story.rb
smo proverili da li je trenutni korisnik administrator:
def self.view_premissions(current_user)
current_user.role.admin? ? Story.all : current_user.stories
end
Pomoću podataka koje smo upravo prikupili možemo stvoriti sledeći test GET stories#index:
describe "GET stories#index" do
context "when the user is an admin" do
it "should list titles of all stories" do
admin = create(:admin)
stories = create_list(:story, 10, user: admin)
login_as(admin, scope: :user)
visit stories_path
stories.each do |story|
page.should have_content(story.title)
end
end
end
context "when the user is not an admin" do
it "should list titles of users own stories" do
user = create(:user)
stories = create_list(:story, 10, user: user)
login_as(user, scope: :user)
visit stories_path
stories.each do |story|
page.should have_content(story.title)
end
end
end
end
Kao što vidite, stvorili smo dva različita konteksta za svaku korisničku ulogu (admin, i ne admin). Administratorski korisnik će moći da vidi sve naslove priča, s druge strane, standardni korisnici mogu da vide samo svoje.
Upotreba opcija create(:user)
i create_list(:story, 10, user: user)
možete kreirati korisnika i deset različitih priča za tog korisnika. Novostvoreni korisnik će se prijaviti login_as(user, scope: :user)
i posetiti stories_path
stranicu, gde može videti sve naslove priča, u zavisnosti od njegove trenutne uloge page.should have_content(story.title)
.
Još jedan odličan način za stvaranje novih korisnika je korišćenje blokova let ili before , to su dva različita načina za pisanje DRY testova.
#show
Na sličan način možete napisati testove metode #show. Jedina razlika je u tome što želite da pristupite stranici koja prikazuje priču koju želite da pročitate.
describe "GET stories#show" do
it "should render stories#show template" do
user = create(:user)
story = create(:story, user: user)
login_as(user, scope: :user)
visit story_path(story.id)
page.should have_content(story.title)
page.should have_content(story.content)
end
end
Još jednom želimo da stvorimo korisnika create(:user)
i priču create(:story, user: user)
. Stvoreni korisnik će se prijaviti i posetiti stranicu koja sadrži priču na osnovu stori.id visit story_path(story.id)
.
#new i #create
Za razliku od ostalih, ovaj metod stvara novu priču. Proverimo sledeću akciju ustories_controller.rb
# GET stories#new
def new
@story = Story.new
end
# POST stories#create
def create
@story = Story.new(story_params)
if @story.save
redirect_to story_path(@story), success: "Story is successfully created."
else
render action: :new, error: "Error while creating new story"
end
end
private
def story_params
params.require(:story).permit(:title, :content)
end
new
Akcija renderuje stories#new šablon, to je forma da popunite pre nego što se kreira nova priča pomoću akcije create
. Nakon uspešnog kreiranja, priča će biti sačuvana u bazi podataka.
describe "POST stories#create" do
it "should create a new story" do
user = create(:user)
login_as(user, scope: :user)
visit new_stories_path
fill_in "story_title", with: "Ruby on Rails"
fill_in "story_content", with: "Text about Ruby on Rails"
expect { click_button "Save" }.to change(Story, :count).by(1)
end
end
Ovog puta kreirani i prijavljeni korisnik će posetiti stranicu na kojoj može stvoriti novu priču visit new_stories_path
. Sledeći korak je popunjavanje obrasca naslovom i sadržajem fill_in "...", with: "..."
. Jednom kada kliknemo na dugme Spremi click_button "Save"
, broj ukupnih priča će se povećati za jednu change(Story, :count).by(1)
, što znači da je priča uspešno kreirana.
#update
Svi žele da mogu da ažuriraju svoje priče. To se lako može učiniti na sledeći način:
def update
if @story.update(story_params)
flash[:success] = "Story #{@story.title} is successfully updated."
redirect_to story_path(@story)
else
flash[:error] = "Error while updating story"
redirect_to story_path(@story)
end
end
private
def story_params
params.require(:story).permit(:title, :content)
end
Kada se stvori nova priča, moći ćemo je ažurirati posetom stranici za uređivanje priča.
describe "PUT stories#update" do
it "should update an existing story" do
user = create(:user)
login_as(user, scope: :user)
story = create(:story)
visit edit_story_path(story)
fill_in "story_title", with: "React"
fill_in "story_content", with: "Text about React"
click_button "Save"
expect(story.reload.title).to eq "React"
expect(story.content).to eq "Text about React"
end
end
Baš kao i u prethodnim metodama, novostvoreni prijavljeni korisnik će stvoriti priču i posetiti stranicu za uređivanje priče edit_story_path(story)
. Kada ažuriramo naslov i sadržaj priče, očekuje se da će se promeniti kako smo tražili expect(story.reload.title).to eq "React"
.
#delete
Napokon želimo da možemo da izbrišemo priče koje nam se nisu svidele.
def destroy
authorize @story
if @story.destroy
flash[:success] = "Story #{@story.title} removed successfully"
redirect_to stories_path
else
flash[:error] = "Error while removing story!"
redirect_to story_path(@story)
end
end
Želite da budete sigurni da samo administrator i vlasnik priče mogu da je izbrišu instaliranjem gema 'pundit'.
class StoryPolicy < ApplicationPolicy
def destroy?
@user.role.admin?
end
end
Isprobajmo i ovo.
describe "DELETE stories#destroy" do
it "should delete a story" do
user = create(:admin)
story = create(:story, user: user)
login_as(user, scope: :user)
visit story_path(story.id)
page.should have_link("Delete")
expect { click_link "Delete" }.to change(Story, :count).by(-1)
end
end
Test je napisan na sličan način kao za stories#create, sa velikom razlikom. Umesto da kreiramo priču, mi je brišemo i tako smanjujemo ukupan broj za jedan change(Story, :count).by(-1)
.
Još jednom smo stigli do kraja! Ali ima za vas još mnogo članaka, pretplatite se odmah!