I have the following integration tests written for my application's Devise based authentication:
# password_resets_spec.rb
require 'spec_helper'
describe "PasswordResets" do
it "emails user when requesting password reset" do
user = FactoryGirl.create(:user)
reset_email # or else we'll have the confirmation email in the last assertion
visit new_user_session_path
click_link "password"
fill_in "Email", with: user.email
click_button "Send"
current_path.should eq(new_user_session_path)
page.should have_content "Will receive"
last_email.to.should include(user.email)
end
it "doesn't email invalid user when requesting password reset" do
user = FactoryGirl.create(:user)
reset_email # or else we'll have the confirmation email in the last assertion
visit new_user_session_path
click_link "password"
fill_in "Email", with: 'nobody#example.com'
click_button "Send"
current_path.should eq(user_password_path)
page.should have_content "correct"
last_email.should be_nil
end
end
and:
# registers_spec.rb
require 'spec_helper'
describe "Registers" do
it "should inform the user to confirm account" do
user = FactoryGirl.build(:user)
visit new_user_registration_path
fill_in "Username", with: user.username
fill_in "Email", with: user.email
fill_in "Password", with: user.password
fill_in "Confirm password", with: user.password
click_button "Send"
current_path.should eq(root_path)
page.should have_content "You have been sent"
last_email.to.should include(user.email)
end
end
I am using Sidekiq for background jobs and last_email and reset_email come from the following module:
module MailerMacros
def last_email
ActionMailer::Base.deliveries.last
end
def reset_email
ActionMailer::Base.deliveries.clear
end
end
All three of these specs work fine when deactivating devise-async on the User model. When I switch it on, the password reset specs run OK but the register one complains about last_email being nil and I don't understand why. Is the confirmation mail sent out somehow differently compared to the password reset ones?
Note that I have the require 'sidekiq/testing/inline' line in my spec_helper.rb file so that the email sending is done instantaneously and config.action_mailer.delivery_method = :test is set for my test environment so that no actual email sending is taking place.
I have solved the issue with mhfs' help. The problem was that I had config.use_transactional_fixtures set to true in spec_helper.rb and because of this users were created in a transaction and the after_commit hook which would send the email was never called. Password resets apparently didn't run inside transactions, that's why they worked.
So I had to switch use_transactional_fixtures off and use database_cleaner to keep my database tidy.
Here's what I had to modify:
Add gem 'database_cleaner' to my Gemfile.
Obviously modify spec_helper.rb:
config.use_transactional_fixtures = false
Add the following to spec_helper.rb:
config.before(:each) do
with_transaction_callbacks = example.metadata[:with_transaction_callbacks]
if with_transaction_callbacks
DatabaseCleaner.strategy = :truncation
else
DatabaseCleaner.strategy = :transaction
end
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
And lastly redo my block in registers_spec.rb to read:
describe "Registers" do
it "should inform the user to confirm account", with_transaction_callbacks: true do
[ --- 8< snip --- ]
end
end
The magic happens in the second line.
PS. This Stack Overflow topic as well as the article linked from within it also helped.
Related
i'm new at rails and i'm testing my devise gem User with capybara rspec and factory girl.
Here's spec code:
require 'rails_helper'
RSpec.describe User, type: :request do
it "displays the user's email after successful login" do
user = FactoryGirl.create(:user, email: 'test#test.com', password: 'password')
visit root_url
fill_in 'Email', with: 'test#test.com'
fill_in 'Password', with: 'password'
click_button 'Log in'
expect page.has_content?('jdoe')
end
end
The problem is in
expect page.has_content?('jdoe')
No matter what i put instead 'jdoe' test works perfectly without any errors.
Here's factory:
FactoryGirl.define do
factory :user do
email 'test#test.com'
password 'password'
password_confirmation 'password'
end
end
Maybe I missed something or going with a wrong way. How should I test here?
# spec/features/sessions_spec.rb
require 'rails_helper'
RSpec.feature "Sessions" do
scenario "displays the user's email after successful login" do
user = FactoryGirl.create(:user)
visit root_url
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
expect(page).to have_content("Signed in successfully")
# will give a false postitive if form is rerended?
expect(page).to have_content(user.email)
end
end
A few things here:
Use a feature and not a request spec for end to end testing.
Don't hardcode object attributes. The whole point of factories is that the factory takes care of generating unique data so that the tests don't give false positives due to residual state.
Use expect(page).to instead of expect page.should. expect(page).to sets a subject and uses a RSpec matcher which will show you the text of the page in an error message if it does not match.
I'm having a realy strange situation here.
I created a helper to perform a 'log in' in my integration tests with RSpec/Capybara:
module AuthenticationHelper
def log_in(user = User.new, remember_me = false)
visit new_user_session_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
check("user_remember_me") if remember_me
save_screenshot("/vagrant/screenshot.png")
click_button "Log in"
end
end
As you can see, it has the remember_me argument, with default value false. It works fine in some kind of tests:
context "when 'remember-me' isn't checked" do
before do
log_in create(:user)
reset_session!
visit root_path
end
it "should not stay connected when browser close the session" do
expect(page).to have_selector("a[href='#{destroy_user_session_path}']", count: 0)
end
end
However, when I need to change the remember_me default value calling log_in method, its value simply doesn't change:
context "when 'remember-me' is checked" do
before do
log_in(create(:user), true)
reset_session!
visit root_path
end
it "should stay connected when browser close the session" do
expect(page).to have_selector("a[href='#{destroy_user_session_path}']", count: 1)
end
end
Here is how I configured it in my rails_helper.rb:
.
.
.
require 'helpers/authentication_helper'
RSpec.configure do |config|
config.include AuthenticationHelper, type: :feature
.
.
.
I already tested fixing remember_me with true just to confirm that the method call is the problem.
What I let pass that is causing this behavior?
Testing "remember me" functionality would require the ability to expire a permanent cookie without deleting it (I think that Capybara::Session#reset_session! is deleting all your cookies), and that is a use case that the show_me_the_cookies gem does really well.
I am trying to test my signup and sign in through devise using rspec, capybara and factoryGirl
the problem is that the tests keep failing in the signin test with this message from activeRecord:
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
these are my tests:
describe "GET /users/sign_up" do
it "register user and send email" do
user = FactoryGirl.build(:user)
visit new_user_registration_path
fill_in "Email", :with => user.email
fill_in "Password", :with => user.password
fill_in "Password confirmation", :with => user.password
fill_in "Name", :with => user.name
click_button "Sign up"
ActionMailer::Base.deliveries.last.to.should include(user.email)
page.should_not have_content("Password confirmation")
end
end
and the signin test:
describe "GET /users/sign_in" do
it "try to login email password" do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in "Email", :with => user.email
fill_in "Password", :with => user.password
click_button "Sign in"
page.should have_content("Signed in successfully.")
end
end
while searching for a solution I saw some people added database_cleaner and said that suppose to help. so i added the database_cleaner gem
and added these lines to my spec_helper.rb file:
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
but it still not working.
would love some guidance or idea. thanks!
adding factory girl code:
require 'factory_girl'
FactoryGirl.define do
factory :user do |f|
f.sequence(:email) {|n| "dodo#{n}#gmail.com"}
f.password "secret123"
f.name "dodo"
end
factory :partner do |f|
f.company "spectoos"
end
end
I found my problem.
the test was failing because the user that I created with factoryGirl is not confirmed. and I have the confirmable on for devise.
any after this line:
user = FactoryGirl.create(:user)
I added this line:
user.confirm!
and now its fine.
thanks
i'm run into the problem with testing user sign in procces
here my test
require 'spec_helper'
include Warden::Test::Helpers
include Devise::TestHelpers
describe "UserSignin" do
it "should allow a registered user to sign in" do
user = FactoryGirl.create(:employer, :email => "email#email.com")
user.confirm!
visit "/users/sign_in"
fill_in "Email", :with => user.email
fill_in "Password", :with => "12345678"
click_button "Sign in"
current_path.should == '/employer'
expect(page).to have_content('My Account')
end
end
if it's improtant i'm using device for authentication
in factory i also have :employer factory
problem with authenticate
after clicking sign_in i'v got an error invalid email or password
UPDATE
there was problem with configs for test environment in spec helper
the solution is to set:
DatabaseCleaner.strategy = :truncation
I'm using minitest with factory girl and capybara for integration tests. Capybara works fine when I don't user factory girl to create a user object, like this:
it "logs in a user successfully" do
visit signup_path
fill_in "Email", :with => "joey#ramones.com"
fill_in "Password", :with => "rockawaybeach"
fill_in "Password confirmation", :with => "rockawaybeach"
click_button "Create User"
current_path == "/"
page.text.must_include "Signed up!"
visit login_path
fill_in "Email", :with => "joey#ramones.com"
fill_in "Password", :with => "rockawaybeach"
check "Remember me"
click_button "Log in"
current_path == "/dashboard"
page.text.must_include "Logged in!"
page.text.must_include "Your Dashboard"
end
But as soon as I try to create a user with factory girl weird things start happening, such as the visit method and click_button methods stop working. For instance, there doesn't seem to be anything wrong with this test:
require "test_helper"
describe "Password resets" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "emails user when requesting password reset" do
visit login_path
click_link "password"
fill_in "Email", :with => user.email
click_button "Reset my password"
end
end
And here's my factories.rb:
FactoryGirl.define do
factory :user do |f|
f.sequence(:email) { |n| "foo#{n}#example.com" }
f.password "secret"
f.password_confirmation "secret"
end
end
Here's the actual error that I'm getting:
est_0001_emails user when requesting password reset 0:00:01.624 ERROR
undefined local variable or method `login_path' for #<#<Class:0x007fc2db48d820>:0x007fc2df337e40>
But, visit login_path works fine if I remove #user = FactoryGirl.create(:user)
Is this a bug with Capybara? Or am I doing something wrong here?
I finally got this to work using the DatabaseCleaner gem using DatabaseCleaner.strategy = truncation. Here's what I ended up with:
test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
require "active_support/testing/setup_and_teardown"
class IntegrationTest < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
register_spec_type(/integration$/, self)
def last_email
ActionMailer::Base.deliveries.last
end
def reset_email
ActionMailer::Base.deliveries = []
end
end
class HelperTest < MiniTest::Spec
include ActiveSupport::Testing::SetupAndTeardown
include ActionView::TestCase::Behavior
register_spec_type(/Helper$/, self)
end
Turn.config.format = :outline
# Database cleaner.
DatabaseCleaner.strategy = :truncation
factories.rb
FactoryGirl.define do
sequence :email do |n|
"email#{n}#example.com"
end
factory :user do
email
password "secret"
password_confirmation "secret"
end
end
integration/login_integration_test.rb
require "test_helper"
describe "Login integration" do
it "shouldn't allow an invalid login" do
visit login_path
click_button "Log In"
page.text.must_include "invalid"
end
it "should login a valid user" do
DatabaseCleaner.clean
user = FactoryGirl.create(:user)
visit login_path
within ".session" do
fill_in "session_email", :with => user.email
fill_in "session_password", :with => "secret"
end
click_button "Log In"
page.text.must_include "Logged in!"
save_and_open_page
end
end
Remove login_path and try to use url
for example
visit "/users/login" #login_path