I'm following the Ruby on Rails Tutorial by Michael Hartl (railstutorial.org).
At some point I got tired of tests failing just bescause the tests used old cached versions of classes, so I turned off config.cache_classes in the test environment. That fixed the issue and everything went good for some time.
Until I tried implementing the Integration tests in Chapter 8.4.3. At this point the data entered into the database with
it "should make a new user" do
lambda do
visit signup_path
fill_in "Name", :with => "Example User"
fill_in "Email", :with => "user#example.com"
fill_in "Password", :with => "foobar"
fill_in "Confirmation", :with => "foobar"
click_button
response.should have_selector("div.flash.success",
:content => "Welcome")
response.should render_template('users/show')
end.should change(User, :count).by(1)
end
would remain in the Database after each test, so only the first time this test ran it would work, after that it always fails until i manually empty the database.
Apart from that it worked.
But now in chapter 9, again the integration test fails:
describe "when signed in" do
before(:each) do
#user = Factory(:user)
visit signin_path
fill_in :email, :with => #user.email
fill_in :password, :with => #user.password
click_button
end
it "should have a signout link" do
visit root_path
response.should have_selector("a", :href => signout_path,
:content => "Sign out")
end
This time it just doesn't work, the user is not getting logged in and the resulting page has no sign out link, just the normal sign in link.
When testing this in a webbrowser it works fine.
It took me hours and days of searching the internet and testing different stuff and finally I found the solution: Turning config.cache_classes back on.
Now it works flawlessly.
So can anyone explain to me why config.cache_classes makes the tests fail? And how can I turn off caching without messing up my tests?
Thanks in Advance,
Best regards, Tobias
When you make a Capybara call, it uses rack-test to emulate a call to the rails app. Every time a call is completed, it reloads all rails classes. What this means is that the #user object you created before you called 'visit signin_path' gets nil'd out because all ActiveRecord objects have been reloaded.
When you set cache-classes to true, it tells Rack not to reload the ActiveRecord objects on every request, so your tests pass again.
I believe that if you want the test you wrote above to pass without turning on cache-classes, you should move the '#user = Factory(:user)' line below the 'visit signin_path' line.
I had exactly the same problem and like you setting config.cache_classes to true solved the problem. But caching classes in the test environment really isn't what you want I don't think. I certainly don't understand why caching classes makes the tests pass.
So the way I found to solve this was to install a database cleane as the reason the tests are failing is due to duplicate entries in the test database. https://github.com/bmabey/database_cleaner
Then in your gemfile, in your test group add this.
gem 'database_cleaner', '0.6.6'
then run "bundle install" to install this gem
Then... in your spec_helper.rb file, add this..
RSpec.configure do |config|
.
.
.
.
config.before(:each) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
end
This will clear your test database before each run of your rspec tests.
Hope this helps.
Cheers,
mark.
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
I have quite some issues with testing the features of my rails application using RSpec. I have a company controller, and are using devise to make sure that you need to be logged in to access the controller.
I then have integration tests like the following:
describe "with valid information" do
before do
fill_in "company_address", with: "My special address"
fill_in "company_name", with: "My User"
fill_in "company_contact_email", with: "test#test.com"
fill_in "company_contact_name", with: "Someone Hello"
end
it "should create a company" do
expect { click_button submit }.to change(Company, :count).by(1)
end
end
It, of course, fails because I don't have a user who is logged in. I have tried creating a test helper like described here, and it works well for controllers, but not for integration tests.
What is the best way to log a user in with Devise, prior to these tests?
add file devise.rb to rspec/support
And here is content
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Another way to solve your problem: Rails integration test with the devise gem
I am running my tests for a rails application using test/unit and capybara. I am fairly new to rails, so I'm hoping that I'm missing something obvious. I have the following integration test to fill out a single field and submit a form. This works as expected and the test passes:
test "create post with title only should add post" do
assert_difference('Post.count') do
visit '/posts/new'
fill_in :title, :with => "Sample Post"
click_button 'create Post'
end
assert current_path == post_path(Post.last)
assert page.has_content?("Sample Post")
end
I added a second test, that pretty much copies the previous test but also fills out a second field and checks for the additional input (this fails).
test "create post with title and body should add post" do
assert_difference('Post.count') do
visit '/posts/new'
fill_in :title, :with => "Testing"
fill_in :body, :with => "This is a sample post"
save_and_open_page
click_button 'create Post'
end
save_and_open_page
assert current_path == post_path(Post.last)
assert page.has_content?("Testing")
assert page.has_content?("This is a sample post")
end
When this failed, I added the call to:
save_and_open_page
and found that the form was being filled out with the title value from the previous test and no body value was supplied at all. The name of the test and the assertions match the second test, so this isn't a case of mistaken identity. It seems that Capybara isn't getting the updated values. I also have this code in my test_helper.rb file:
DatabaseCleaner.strategy = :truncation
module ActionController
class IntegrationTest
include Capybara::DSL
self.use_transactional_fixtures = false
teardown do
DatabaseCleaner.clean
Capybara.reset_sessions!
Capybara.use_default_driver
end
end
end
I'm assuming this should clear out values in between tests. Since that clearly wasn't happening, I also tried adding a call to Capybara.rest_sessions! at the end of the first test and that didn't help.
Any advice or help would be greatly appreciated.
I figured it out. I was calling the fill in method with the symbol :title instead of a string for the field id. I needed to be using 'post_title'. I started that way, but was not prefixing the name with the model name so it wasn't found and it started working when I changed to the symbol that I was using in my erb code.
So use:
fill_in 'post_title', :with => "whatever"
instead of
fill_in :title, :with => "whatever"
I have no idea why it's not working. After hours of trying to figure this out I wrote a small test to check if the ActionMailer::Base.deliveries was empty and it was.
When I test my reset form it works and mail is sent but when I run test it doesn't seem to store anything in the array.
require 'spec_helper'
describe "Passwords" do
describe "reset password" do
it "emails user when requesting password reset" do
visit login_path
click_link "Forgot Password?"
response.should render_template "passwords/new"
response.should have_selector :h1, :content => "Reset Password"
fill_in "password_reset[email]", :with => "foobar#gmail.com"
click_button "Reset Password"
response.should render_template "users/new"
response.should have_selector :div, :id => "flash_notice", :content => "Email sent with password reset instructions."
ActionMailer::Base.deliveries.empty?.should be_true
# mail = ActionMailer::Base.deliveries.last
end
end
end
Just found out a great gem to test emails with rspec: https://github.com/bmabey/email-spec
Hope it will help.
I always seem to do silly things like this:
Because I created the passwords_spec file manually and not using the rspec generator I forgot to add "require 'spec_helper'" at the top of the file. Something that the generator would have done automatically.
The last few days I have been figuring out my silly mistakes. Funny how all this happened when I got lazy and decided to build my app first then test after. Well I've learnt from my mistake. Test first!
In the Rails on Rails Tutorial by Michael Hartl, the Request Examples make assertions on the response. I installed the cabybara and steak gem to create acceptance tests. After installing capybara, the requests examples are configured to use capybara. capybara examples have a different syntax and don't recognize the response.
How do I reset the Request Examples to run as RSpec example?
Test Error:
4) Users signup failure should not make a new user
Failure/Error: click_button
wrong number of arguments (0 for 1)
# ./spec/requests/users_spec.rb:13
# ./spec/requests/users_spec.rb:7
Request Example
describe "failure" do
it "should not make a new user" do
lambda do
visit signup_path
fill_in "Name", :with => ""
fill_in "Email", :with => ""
fill_in "Password", :with => ""
fill_in "Confirmation", :with => ""
click_button
response.should render_template('users/new')
end.should_not change(User, :count)
end
end
This might have something to do with rspec-rails request_example_group.rb.
request_example_group.rb
If the capybara lines are commented out, then the Request Examples are not defaulted to capybara.
capybara do
include Capybara
end
In the Acceptance examples, include capybara.
require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
feature "This Is My First Feature", %q{
In order to ...
As a ...
I want to ...
} do
include Capybara
scenario "Scenario name" do
visit signup_path
fill_in "Name", :with => ""
fill_in "Email", :with => ""
fill_in "Password", :with => ""
fill_in "Confirmation", :with => ""
click_button "Sign up"
page.has_content?('welcome to the sample app')
end
end
I ended up forking rspec-rails and commenting out the capybara lines.
In the Gemfile, the gem installed from the fork.
Why bother? We want both white box and black box testing.
As you correctly point out in your answer, rspec-rails will include Capybara if it is installed. I think they're assuming that it never hurts to include Capybara, just in case you need it. Is that a problem for you? In other words, what specifically is the Capybara DSL conflicting with?
(This might be also be worth bringing up on the RSpec mailing list.)
As for the error you posted, it seems that you are simply missing an argument to the click_button method.