I have Rspec testing controller actions via visiting pages.
And when I'm declaring new object through let() for create and edit actions - it's all fine. But for delete it doesn't work and I have to declare instance variable to make it pass.
My goal is to get rid of instance variable declaration in delete action.
It stops me from finishing re-factoring and I want to know why this happen?
Maybe it's somehow related to Capybara scope.
Failed tests looks like:
Failure/Error: it {should have_content('delete')}
expected #has_content?("delete") to return true, got false
and
Failure/Error: expect{click_link('delete')}.to change(Crew, :count).by(-1)
Capybara::ElementNotFound:
Unable to find link "delete"
My whole test
require 'spec_helper'
describe 'CrewPage' do
subject {page}
let(:user) {FactoryGirl.create(:user)}
let(:crew) {Crew.create(name: name, secondname: secondname, rate: rate)}
let(:name) {'Name'}
let(:secondname) {'First'}
let(:rate) {1000}
before {sign_in(user)}
#==============================New page===========>>
describe 'new crew member page' do
before {visit new_crew_path}
it {should have_title('New member')}
let(:submit) {"Create"}
context 'invalid creation' do
it 'should not create crew member' do
expect{click_button submit}.not_to change(Crew, :count)
end
end
context 'valid crew member creation' do
before do
fill_in 'Name', with: name
fill_in 'Secondname', with: secondname
fill_in 'Rate', with: rate
end
it 'should create crew member' do
expect{click_button submit}.to change(Crew, :count).by(1)
end
end
end
#==============================Show page===========>>
describe 'show page' do
before {visit crew_path(crew)}
it {should have_title("#{crew.combine_names} profile")}
end
#==============================Edit page===========>>
describe 'edit page' do
let(:reload_n) {name*2}
let(:reload_sn) {secondname*2}
let(:reload_r) {rate*2}
before {visit edit_crew_path(crew)}
it {should have_title('Edit panel')}
context 'successfull edit' do
before do
fill_in 'Name', with: reload_n
fill_in 'Secondname', with: reload_sn
fill_in 'Rate', with: reload_r
click_button('Save')
end
it {should have_content(reload_n)}
it {should have_content(reload_sn)}
it {should have_content(reload_r)}
end
end
#==============================Delete action===========>>
describe 'delete action from index page' do
before do
#crew = Crew.create(name: name, secondname: secondname, rate: rate)
visit root_path
end
it {should have_content('delete')}
it 'should delete crew member' do
expect{click_link('delete')}.to change(Crew, :count).by(-1)
end
end
end
let block is not executed unless it is called in the test itself. Because you are not using this variable prior to visit_page, crew model is not created and hence it is not displayed on the page capybara is playing with. If you want it to execute before each test, you have to call let! instead:
let!(:crew) { Crew.create(name: name, secondname: secondname, rate: rate)}
Related
My application manages BusinessFlows, which can only be created within a parent BusinessArea. The 'new' method in BusinessFlows controller is:
def new
#business_area = BusinessArea.find(params[:business_area_id])
#business_flow = BusinessFlow.new
end
The Factory used for testing works fine, and creates the parent BusinessArea, and the the BusinessFlow as expected:
FactoryBot.define do
factory :business_flow do
association :parent, factory: :business_area
playground_id {0}
name {"Test Business Flow"}
code {Faker::Code.unique.rut}
description {"This is a test Business Flow used for unit testing"}
created_by {"Fred"}
updated_by {"Fred"}
owner_id {0}
responsible_id {0}
deputy_id {0}
organisation_id {0}
status_id {0}
end
end
But when it comes to test if the 'new' view can be displayed, the controller raises an error due to missing params[:business_area_id]:
ActiveRecord::RecordNotFound:
Couldn't find BusinessArea without an ID
Here is the feature test script:
require 'rails_helper'
RSpec.describe BusinessFlow, type: :request do
include Warden::Test::Helpers
describe "Business Flows pages: " do
let(:bf) {FactoryBot.create(:business_flow)}
context "when not signed in " do
it "should propose to log in when requesting index view" do
get business_flows_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting new view" do
get new_business_flow_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting edit view" do
get edit_business_flow_path(bf)
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show view" do
get business_flow_path(bf)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_flows_path
expect(response).to render_template(:index)
end
it "should display new view" do
get new_business_flow_path(bf.parent.id)
expect(response).to render_template(:_form)
end
it "should display edit view" do
get edit_business_flow_path(bf)
expect(response).to render_template(:_form)
end
it "should display show view" do
get business_flow_path(bf)
expect(response).to render_template(:show)
end
end
end
end
Only the 'new' method test in the context 'when signed in' fails.
Can you please help me solving this?
Thanks a lot!
it "should display new view" do
get new_business_flow_path(business_area_id: bf.parent)
expect(response).to render_template(:_form)
end
Rails thinks the id you're passing is part of the business flow path (and it isn't), it needs to be passed as a param I think.
I am testing the avaibility of BusinessArea views in the context of signed in / not signed in user.
At the beginning of the test, I create the business area object (test_ba) thanks to the factory, which returns the object.
I 'puts' the test_ba.id to see it created.
Then I request the tested view.
require 'rails_helper'
RSpec.describe BusinessArea, type: :request do
include Warden::Test::Helpers
describe "Business Areas pages: " do
test_ba = FactoryBot.create(:business_area)
puts test_ba.id
context "when not signed in " do
it "should propose to log in when requesting index" do
get business_areas_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show" do
puts test_ba.id
get business_area_path(test_ba)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_areas_path
expect(response).to render_template(:index)
end
it "should display business area" do
puts test_ba.id
get business_area_path(test_ba)
expect(response).to render_template(:show)
end
end
end
end
The test seems to run correctly, but the last step fails due to missing record!?! The ouput returns:
>rspec spec/requests/business_areas_spec.rb
67
.67
..67
F
Failures:
1) BusinessArea Business Areas pages: when signed in should display business area
Failure/Error: #business_area = BusinessArea.find(params[:id])
ActiveRecord::RecordNotFound:
Couldn't find BusinessArea with 'id'=67
# ./app/controllers/business_areas_controller.rb:159:in `set_business_area'
# ./spec/requests/business_areas_spec.rb:35:in `block (4 levels) in <top (required)>'
Finished in 2.07 seconds (files took 13.05 seconds to load)
4 examples, 1 failure
Failed examples:
rspec ./spec/requests/business_areas_spec.rb:33 # BusinessArea Business Areas pages: when signed in should display business area
Can you help me find what's wrong with this?
RSpec has the let and let! methods that create memoized helpers that you should use to setup your test dependency. let is lazy loading (the block is not evaluated until you reference it) while let! is not.
require 'rails_helper'
RSpec.describe BusinessArea, type: :request do
include Warden::Test::Helpers
describe "Business Areas pages: " do
let!(:test_ba){ FactoryBot.create(:business_area) }
context "when not signed in " do
it "should propose to log in when requesting index" do
get business_areas_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show" do
puts test_ba.id
get business_area_path(test_ba)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_areas_path
expect(response).to render_template(:index)
end
it "should display business area" do
puts test_ba.id
get business_area_path(test_ba)
expect(response).to render_template(:show)
end
end
end
end
But wah! Why doesn't my code work?
In RSpec (and in any good test framework) each example runs in isolation and has its own setup and teardown. This includes rolling back the database or clearing it. RSpec does not even run the tests in consecutive order by design.
The record you are defining in the outer context will not be created for each test run. After the first example when the db is is rolled back its gone.
If you want to set something up for each test use before:
require 'rails_helper'
RSpec.describe BusinessArea, type: :request do
include Warden::Test::Helpers
describe "Business Areas pages: " do
before do
#test_ba = FactoryBot.create(:user)
end
context "when not signed in " do
it "should propose to log in when requesting index" do
get business_areas_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show" do
puts #test_ba.id
get business_area_path(test_ba)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
#test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_areas_path
expect(response).to render_template(:index)
end
it "should display business area" do
puts #test_ba.id
get business_area_path(test_ba)
expect(response).to render_template(:show)
end
end
end
end
But let / let! are preferred for setting up simple dependencies.
I have basic rspec tests for my Client model. And it broke after I assigned it to current_user. I have problems figuring out what's wrong. Looking for some help.
Problem exist with show, edit and delete actions
require 'spec_helper'
describe 'ClientPage' do
subject {page}
let(:user) {FactoryGirl.create(:user)}
let(:client) {user.clients.build(client_name: client_name, client_secondname: client_secondname,
budget: budget, project: project)}
let(:client_name) {'Client'}
let(:client_secondname) {'First'}
let(:budget) {3000}
let(:project) {'Project'}
before {sign_in(user)}
#==============================New page===========>>
describe 'new client page' do
before {visit new_client_path}
it {should have_title('New client')}
let(:submit) {"Create"}
describe 'create' do
context 'invalid creation' do
it 'should not create client' do
expect{click_button submit}.not_to change(Client, :count)
end
end
context 'valid client creation' do
before do
fill_in 'Client name', with: client_name
fill_in 'Client secondname', with: client_secondname
fill_in 'Budget', with: budget
fill_in 'Project', with: project
end
it 'should create client' do
expect{click_button submit}.to change(Client, :count).by(1)
end
end
end
end
#==============================Show page===========>>
describe 'show' do
before {visit client_path(client)}
it {should have_title("#{client.combine_names} profile")}
end
#==============================Edit page===========>>
describe 'edit' do
let(:reload_cn) {client_name*2}
let(:reload_csn) {client_secondname*2}
let(:reload_bgt) {client.budget*2}
let(:reload_prg) {client.project*2}
before {visit edit_client_path(client)}
it {should have_title('Edit client panel')}
context 'successfull edit' do
before do
fill_in 'Client name', with: reload_cn
fill_in 'Client secondname', with: reload_csn
fill_in 'Budget', with: reload_bgt
fill_in 'Project', with: reload_prg
click_button('Save')
end
it {should have_content(reload_cn)}
it {should have_content(reload_csn)}
it {should have_content(reload_bgt)}
it {should have_content(reload_prg)}
end
end
#==============================Delete action===========>>
describe 'delete' do
before do
#client = user.clients.build(client_name: client_name, client_secondname: client_secondname, budget: budget, project: project)
visit root_path
end
it 'should delete client' do
expect{#client.delete}.to change(Client, :count).by(-1)
end
end
end
Solved. I've missed one thing - build do not save to database so example won't have an id.
create or FactoryGirl hepls.
Testing filling in the contact form without filling in one of the required fields in one test and then filling in the honey_pot field in another test. The tests when valid info is filled in are passing, but the two other specs are failing.
Update I've been running the test a couple of times and sometimes all the tests pass. Other times, the same specs fail.
Terminal output
3) Pages Home page it should behave like all static pages Footer Contact Form when honeypot is filled does not send an email
Failure/Error: expect(ActionMailer::Base.deliveries).to be_empty
expected empty? to return true, got false
Shared Example Group: "all static pages" called from ./spec/requests/pages_spec.rb:69
# ./spec/requests/pages_spec.rb:46:in `block (6 levels) in <top (required)>'
4) Pages Home page it should behave like all static pages Footer Contact Form when fields are not filled does not send an email
Failure/Error: expect(ActionMailer::Base.deliveries).to be_empty
expected empty? to return true, got false
Shared Example Group: "all static pages" called from ./spec/requests/pages_spec.rb:69
# ./spec/requests/pages_spec.rb:38:in `block (6 levels) in <top (required)>'
pages_spec.rb
require 'spec_helper'
describe "Pages" do
subject { page }
shared_examples_for "all static pages" do |path_name|
before { visit send(path_name) }
describe "Footer" do
describe "Contact Form" do
it { should have_selector('h7', text: 'Contact Us') }
context "when a valid message" do
it "sends an email" do
post contact_create_path, message: attributes_for(:message)
expect(ActionMailer::Base.deliveries.last.to).to eq(["#{ENV["MVP_USERNAME"]}"])
ActionMailer::Base.deliveries.clear
end
end
context "when fields are not filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, name: '', body: '')
expect(ActionMailer::Base.deliveries).to be_empty
ActionMailer::Base.deliveries.clear
end
end
end
context "when honeypot is filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, sweet_honey: 'bot')
expect(ActionMailer::Base.deliveries).to be_empty
ActionMailer::Base.deliveries.clear
end
end
end
end
end
describe "Home page" do
before { visit root_path }
it_should_behave_like "all static pages", :root_path
it { should have_text('Quality Cars') }
it { should have_title('Misawa Used Cars - Misawa Auto Sales') }
describe "Send a message" do
before do
fill_in "Name", with: 'name'
fill_in "Email", with: 'email#example.com'
fill_in "Phone", with: '999-9999-9999'
fill_in "Body", with: 'Hello'
click_button "Send"
end
describe "after the message is sent" do
it "should render the desired page with a flash" do
expect(page).to have_text('Quality Cars')
expect(page).to have_title('Misawa Used Cars - Misawa Auto Sales')
expect(page).to have_selector('.alert-box.success')
end
end
end
end
end
I was able to get the specs passing by clearing the ActionMailer::Base.deliveries before and after each of the specs where I tested the deliveries. I also rewrote the tests to make it DRYer using the before(:each) and after(:each) methods describe in Rspec's documentation. Update Even better, around(:each)
Improved Specs
describe "Contact Form" do
it { should have_selector('h7', text: 'Contact Us') }
describe "send a message" do
around(:each) { ActionMailer::Base.deliveries.clear }
context "when a valid message" do
it "sends an email" do
post contact_create_path, message: attributes_for(:message)
expect(ActionMailer::Base.deliveries.last.to).to eq(["#{ENV["MVP_USERNAME"]}"])
end
end
context "when fields are not filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, name: '', body: '')
expect(ActionMailer::Base.deliveries).to be_empty
end
end
context "when honeypot is filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, sweet_honey: 'bot')
expect(ActionMailer::Base.deliveries).to be_empty
end
end
end
end
I have problem with my rspec code. I write some tests. I use syntax like: subject {page} and then i want to write test's in this style: it {should have_content()} but when I run rspec it show's error:
Failure/Error: it{ should have_content("Post associated with #{category.name}") }
NoMethodError:
undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x00000007c86ea8>
This is my all file:
require 'spec_helper'
describe "Categories Pages" do
subject {page}
let(:user) {FactoryGirl.create(:user)}
before {sign_in user}
let(:category) {FactoryGirl.create(:category)}
let(:p3 ) {FactoryGirl.create(:post, user: user, content: "Foo", title: "Bar", category: category)}
describe "Categories show page" do
before do
visit post_path(p3)
click_link 'Test Category'
end
it "should has elements" do
current_path.should == category_path(category)
it{ should have_content("Post associated with #{category.name}") }
expect(page).to have_content(p3.content)
expect(page).to have_link(p3.title, href: post_path(p3))
expect(page).to have_content(p3.comments.count)
expect(page).to have_link(p3.category.name, href: category_path(p3.category))
expect(page).to have_link(p3.user.name, href: user_path(user))
expect(page).to have_link('All Categories', href: categories_path)
expect(page).to have_title(full_title('Test Category'))
end
end
describe "Categories index page" do
before do
visit post_path(p3)
click_link 'Test Category'
click_link "All Categories"
end
it "should have elements" do
expect(page).to have_link('Test Category', href: category_path(category))
expect(page).to have_selector('h1', text: 'All Categories')
expect(page).to have_title(full_title('All Categories'))
end
end
end
Please help.
You can't have an it inside another it.
The problem is in this line:
it{ should have_content("Post associated with #{category.name}") }
Simply remove the it, or move it to outside of the other it block.
page.should have_content("Post associated with #{category.name}")
It is a good idea have one assertion per test. Check better specs
I don't think you can nest it blocks. Pull this line
it{ should have_content("Post associated with #{category.name}") }
out of the surrounding it block and make it a stand-alone test in the describe block.