I have a describe block like this:
describe "Documents" do
subject { page }
let (:course) { FactoryGirl.create(:course) }
describe "new" do
before do
visit new_course_document_path(course)
fill_in "Name", with: "TestDocument"
attach_file "Original document", "#{Rails.root}/spec/fixtures/03_GUI_concurrency.pdf"
end
it { should have_selector('title', text:"Upload document")}
it { should have_selector('h1', text:"Upload document")}
describe "when clicking upload" do
before { click_button "Upload Document" }
it "should have the document name" do
subject.should have_selector('p', text: "TestDocument")
end
it "should have 22 pages" do
subject.should have_selector('.page', count: 22)
end
describe "when visiting the course page" do
before { visit course_path(course) }
it { should have_selector 'li', text: "TestDocument"}
end
end
end
The test is quite expensive since significant work is done on saving the document. That's fine, but it's even slower since it's actually doing that upload 3 times. So, the obvious thing to do is to make the before blocks into before :all blocks - but when I do that, only the first it {} block is executed correctly and the ones afterward are executing on an empty page so they fail.
Are before :all blocks supposed to work with capybara, and if so what am I doing wrong here?
Capybara resets session after each example run, so when you moved visit new_course_document_path(course) into before (:all) block, you had nothing to visit starting from a second example.
I would recommend to run only that tests you're working on. This could be achieved with a RSpec tag option or guard-rspec, and it'll save you a LOT of time.
Related
I am experiencing strange very test behavior, with logged in state being handled inconsistently.
The spec logs a user in, visits a (nested or un-nested) index page, and checks that the correct content is displayed. Records are fetched asynchronously, though I don't think this should have an impact.
When each spec is run individually, they each pass. When all specs are run together, they fail because the expected content is missing. Using save_and_open_page reveals this is because the login page is being rendered, rather than the expected index page.
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
The tests look something like this
let(:user) {create :user}
let(:team) {create :team}
let(:country) {create :country}
before :each do
login_as( user, scope: :user )
end
describe 'unnested' do
it 'should have the expected content', :js do
visit users_path
is_expected.to have_content "some content on the page"
end
end
describe 'nested by team' do
it 'should have the expected content', :js do
visit team_users_path(team)
is_expected.to have_content "some content on the page"
end
end
describe 'nested by nationality' do
it 'should have the expected content', :js do
visit country_users_path(country)
is_expected.to have_content "some content on the page"
end
end
The specs all require javascript (I don't know whether that is important here).
Authentication is handled by Devise, and my rails_helper.rb includes
config.append_after(:each) do
DatabaseCleaner.clean
Warden.test_reset!
end
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
It took a long time to get to the bottom of this. Posting this hear in case it is of help to anyone else encountering the same issue.
After much searching I eventually found this small mention that login_as may not work with Poltergeist when js is enabled on your test scenarios.
I tried the suggested fix to deal with shared DB connections. Unfortunately this resulted in the following errors:
PG::DuplicatePstatement at /session/users/signin
ERROR: prepared statement "a1" already exists
I tried using the Transactional Capybara gem, but this did not seem to work well with Poltergeist.
Eventually I abandonned login_as completely, and instead wrote a short method that visits the login page, fills in email and password, and logs in that way.
This solution appears to be working. It adds a little overhead, so I'm only using it for tests with JS.
If you are using Capybara gem then there is no need to use :js with test cases
What I did if this helps-
scenario "visit with user signed in" do
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
visit "/"
expect(current_path).to eq('/')
expect(page).to have_title "Some Random Title"
end
The other way you can login user using feature specs like-
feature 'User signs in' do
before :each do
#user = FactoryGirl.create(:user)
end
scenario "Signing in with correct credentials" do
visit "/"
fill_in "Email", with: #user.email
fill_in "Password", with: #user.password
click_button "Log In"
expect(current_path).to eq("/login/useremail/verification")
expect(page).to have_content "Signed in successfully"
end
end
If your pages are ajax then refer to this https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara
Let's say I have various RSpec context blocks to group tests with similar data scenarios.
feature "User Profile" do
context "user is active" do
before(:each) { (some setup) }
# Various tests
...
end
context "user is pending" do
before(:each) { (some setup) }
# Various tests
...
end
context "user is deactivated" do
before(:each) { (some setup) }
# Various tests
...
end
end
Now I'm adding a new feature and I'd like to add a simple scenario that verifies behavior when I click a certain link on the user's page
it "clicking help redirects to the user's help page" do
click_on foo_button
expect(response).to have('bar')
end
Ideally I'd love to add this test for all 3 contexts because I want to be sure that it performs correctly under different data scenarios. But the test itself doesn't change from context to context, so it seems repetitive to type it all out 3 times.
What are some alternatives to DRY up this test set? Can I stick the new test in some module or does RSpec have some built in functionality to let me define it once and call it from each context block?
Thanks!
You can use shared_examples ... define them in spec/support/shared_examples.rb
shared_examples "redirect_help" do
it "clicking help redirects to the user's help page" do
click_on foo_button
expect(response).to have('bar')
end
end
Then in each of your contexts just enter...
it_behaves_like "redirect_help"
You can even pass a block to it_behaves_like and then perform that block with the action method, the block being unique to each context.
Your shared_example might look like...
shared_examples "need_sign_in" do
it "redirects to the log in" do
session[:current_user_id] = nil
action
response.should render_template 'sessions/new'
end
end
And in your context you'd call it with the block...
describe "GET index" do
it_behaves_like "need_sign_in" do
let(:action) {get :index}
end
...
I can't for the life of me figure out why these tests are failing.
When a user puts in their email/password and hits the Log in button, they are redirected to their profile page which puts their first name in the title and displays their first name on the page. It also shows a link to their profile and a sign out link. When I went through the steps in the browser everything was where it should be, but when rspec runs it continues to fail.
What I find really odd is that when I run a user_page_spec test that tests the same elements, those all pass.
I figure it has to do with either the click_button part or the "redirect_to user" in the controller, but any insight would be much appreciated.
Here are the tests-
Passing tests in user_pages_spec.rb-
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
before { visit user_path(user) }
it { should have_selector('h1', text: user.firstName) }
it { should have_selector('title', text: user.firstName) }
end
Failing tests in authentication_pages_spec.rb -
require 'spec_helper'
describe "Authentication" do
describe "sign in" do
.
.
.
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Log in"
end
it { should have_selector('title', text:user.firstName) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
describe "followed by signout" do
before { click_link "Sign out" }
it { should have_link('Home') }
end
end
end
end
Yup. It's always the simplest of oversights that cause the biggest of headaches.
Here is what happened.
Rather than using the following-
describe "page" do
it "should have something" do
page.should have_selector('')
end
end
Rspec lets you define a subject -
subject { page }
Which allows you to simplify the first code block to the following-
subject { page }
describe "page" do
it { should have_selector('') }
end
This allows you to run multiple tests which reference the page without all the extra typing.
I left out the subject { page } at the very top, so none of my it {} blocks knew what to reference. As soon as that was added in, all tests passed with no problems.
Hope this helps someone else out in the future.
I'm new to ruby/rails and doing a turorial I was wondering how I could solve my little problem...
I have a describe "with valid information" which tests what should or should not happen when the user signs in with valid info.
In this procedure, I want to be able to verify that some links exists on the new page. In the tuto, there are only 2 links so we do it like that:
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
But what if we have let's say 10 links? I think it would be easier to have a helper no? But I can't figure out how to write it. This is what I came with...
lol = Hash[ 'Profile' => 'user_path(user)',
'Sign out' => 'signin_path']
it { should have_list_of_links(lol) }
then in a $PROJECT/spec/support/utilities.rb file, I define the new *have_list_of_links* function:
RSpec::Matchers.define :have_list_of_links do |lol|
match do |page|
lol.each_pair do |label, link|
page.should have_link(label, href: link)
end
end
end
I know it's not the right way to do it but I can't figure out how to do it...
Thanks.
There is two small problems I can see here. First use let syntax to define memoized variables inside rspec. Second drop the parentheses around *_path helpers when you build your hash.
So insted:
lol = Hash[ 'Profile' => 'user_path(user)',
'Sign out' => 'signin_path']
have:
let(:lol) { Hash[ 'Profile' => user_path(user),
'Sign out' => signin_path] }
Your description block could be:
describe "with valid information" do
let(:lol) { Hash[ 'Profile' => user_path(user),
'Sign out' => signin_path] }
it { should have_list_of_links(lol) }
end
As of a side effect I'll show you small example. Given you have matcher defined in $PROJECT/spec/support/utilities.rb file, application routes etc... are set up correctly and you have links in your view.
describe "Users pages" do
before { visit root_path }
let(:values) { Hash['Index' => users_path,
'Sign out' => signout_path,
'Sign in' => signin_path] }
subject { page }
describe "with valid informations" do
it { should have_list_of_links(values) }
end
end
running rspec:
> rspec
.
Finished in 0.00267 seconds
1 example, 0 failures
Randomized with seed 67346
running rspec -f documentation
>rspec -f documentation
Users pages
with valid informations
should have link "Sign in"
Finished in 0.00049 seconds
1 example, 0 failures
Randomized with seed 53331
This is not clear and misleading, specially the documentation switch. Its a common practice to run a rspec -f documentation on new application You just put your hands on(if they use rspec ofc). To better understands what is going on.
If you instead have:
describe "Users pages" do
before { visit root_path }
subject { page }
describe "with valid informations" do
it { should have_link('Index', href: users_path) }
it { should have_link('Sign out', href: signout_path) }
it { should have_link('Sign in', href: signout_path) }
end
end
running rspec:
>rspec
...
Finished in 0.0097 seconds
3 examples, 0 failures
Randomized with seed 53347
running rspec -f documentation
>rspec -f documentation
Users pages
with valid informations
should have link "Index"
should have link "Sign out"
should have link "Sign in"
Finished in 0.00542 seconds
3 examples, 0 failures
Randomized with seed 40120
I personally like second case(more verbose one). Its value increasing when number of test are growing and test structures are getting more complicated. You can simply run rspec -f documentation to learn how to use application without even going to user manual/tutorial.
While going through Ruby On Rails Tutorial by Michael Hartl,in the section where the author writes integration test to validate his Signup page, he has used code spinet bellow. I got what the code does but couldn't get my head around the 'how' part i.e. couldn't understand order of execution.
expect { click_button "Create my account" }.not_to change(User, :count)
Can someone please explain the semantics of the above chain of methods and blocks and how they fit together?
You'd use expect ... change to verify that a particular method call changes -- or does not change -- some other value. In this case:
expect { click_button "Create my account" }.not_to change(User, :count)
will cause rspec to do the following:
Run User.count and note the value returned. (This can be specified as a receiver and a method name, like (User, :count) in your example, or as an arbitrary block of code, like { User.count }.
Run click_button "Create my account" which is a Capybara method that simulates a mouse click on a link.
Run User.count again.
Compare the results of #1 and #3. If they differ, the example fails. If they're the same, it passes.
Other ways to use expect ... change:
expect { thing.destroy }.to change(Thing, :count).from(1).to(0)
expect { thing.tax = 5 }.to change { thing.total_price }.by(5)
expect { thing.save! }.to raise_error
expect { thing.symbolize_name }.to change { thing.name }.from(String).to(Symbol)
Some docs are here.
How it does this is a bit arcane, and it's not at all necessary to understand how it works in order to use it. A call to expect is defining a structure for rspec to execute, using rspec's own custom DSL and system of "matchers." Gary Bernhardt has a rather neat screencast in which he argues that the mystery of rspec actually falls out naturally from a dynamic language like ruby. It's not a good introduction to using rspec, but if you're curious about how it all works, you might find it interesting.
UPDATE
After seeing your comment on another answer, I'll add a bit about the order of operations. The unintuitive trick is that it's the matcher (change in this case) that executes all of the blocks. expect has a lambda, not_to is an alias for should_not whose job is to pass the lambda on to the matcher. The matcher in this case is change which knows to execute its own argument once, then execute the lambda that it was passed (the one from expect), then run its own argument again to see if things changed. It's tricky because the line looks like it should execute left to right, but since most of the pieces are just passing around blocks of code, they can and do shuffle them into whatever order makes the most sense to the matcher.
I'm not an expert on the rspec internals, but that's my understanding of the basic idea.
Here's an excerpt from from Ryan Bates Railscast on Request Specs and Capybara
require 'spec_helper'
describe "Tasks" do
describe "GET /tasks" do
it "displays tasks" do
Task.create!(:name => "paint fence")
visit tasks_path
page.should have_content("paint fence")
end
end
describe "POST /tasks" do
it "creates a task" do
visit tasks_path
fill_in "Name", :with => "mow lawn"
click_button "Add"
page.should have_content("Successfully added task.")
page.should have_content("mow lawn")
end
end
end
And here's an excerpt from the docs on RSPec Expectations
describe Counter, "#increment" do
it "should increment the count" do
expect{Counter.increment}.to change{Counter.count}.from(0).to(1)
end
# deliberate failure
it "should increment the count by 2" do
expect{Counter.increment}.to change{Counter.count}.by(2)
end
end
So basically, the
expect { click_button "Create my account" }.not_to change(User, :count)
is part RSpec:
expect {...}.not_to change(User, :count)
and part Capybara
click_button "Create my account"
(Here's a link to the Capyabara DSL -- you can search for click_button)
It sounds like you're looking for an overall example with them both. This isn't a perfect example, but it could look something like this:
describe "Tasks" do
describe "GET /tasks" do
it "displays tasks" do
expect { click_button "Create my account" }.not_to change(User, :count)
end
end
end