Motivated by the discussion in this question, I want to write a log-in method for my integration tests. In my test_helper.rb
I even found such a method, but it is defined inside a ActiveSupport::TestCase
class, and my test inherits from ActionDispatch::IntegrationTest
. So I copied the method and put it (inside test_helper.rb
) into the ActionDispatch::IntegrationTest
class. But it doesn't work and my tests fail with this message:
Capybara::ExpectationNotMet: expected "data:," to include "Study | Word Up"
It never actually opens the app in the browser as it would before.
So my question is, can I use such a shortcut at all in integration tests, and if yes, how?
I am using the build in authentication with has_secure_password
and the mechanism shown by Michael Hartl in his Railstutorial.
Here is my test_helper.rb
:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'minitest/rails'
require 'minitest/rails/capybara'
require 'capybara/rails'
require 'capybara/poltergeist'
class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
fixtures :all
# Logs in a test user.
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, session: { email: user.email,
password: password,
remember_me: remember_me }
else
session[:user_id] = user.id
end
end
private
def integration_test?
defined?(post_via_redirect)
end
end
class ActionDispatch::IntegrationTest
include Capybara::DSL
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, session: { email: user.email,
password: password,
remember_me: remember_me }
else
session[:user_id] = user.id
end
end
end
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end
Capybara.current_driver = :selenium_chrome
Capybara.default_wait_time = 5
The test itself looks like this:
require 'test_helper'
class StudyCapybaraTest < ActionDispatch::IntegrationTest
def setup
@user = users(:archer)
@vocabs = @user.vocabs
log_in_as @user
# visit login_path
# fill_in "session_email", with: @user.email
# fill_in "session_password", with: 'password'
# click_button "session_commit"
end
....
end
You can't use the #post method with Capybara since it won't get routed to the driver, which is why the browser doesnt open for you. To shortcut login in integration tests, the easiest solution is to install backdoor middleware in the test environment like this - https://robots.thoughtbot.com/faster-tests-sign-in-through-the-back-door That example is for clearance so you'll have to change the
to do whatever you're doing when a users password is verified as valid, but other than that it should work fine. Then you wouldn't actually need a login_as, you could just do
Note -- you should also have tests that do the logging in manually to verify that it works, and its VERY important you only have that middleware in the test environment