How can I write a shortcut for signing a user in i

2019-08-13 07:28发布

问题:

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

回答1:

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

@env[:clearance].sign_in(user)

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

visit page_being_tested_path(as: user.id)

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