如何使用RSpec模拟登录?

时间:2012-04-12 10:32:49

标签: ruby-on-rails rspec factory-bot

我已经使用Rails几年了,并且已经制作了一些正在制作的可通过的应用程序。我总是避免做任何测试,但我决定纠正这个问题。我正在尝试为我已经开始运行但正在不断修改的工作编写的应用程序编写一些测试。我担心任何改变都会破坏事情,所以我想让一些测试运行起来。我已经阅读了RSpec的书,观看了一些截屏视频,但我很难开始使用(这让我感到很震惊,因为一旦你真的完成了它,你就会明白这一点。)

我正在尝试编写应该对ReportsController进行简单测试的内容。我的应用程序的问题在于,整个事情几乎都位于身份验证层之后。如果你没有登录就没有任何作用所以我必须先模拟一个登录才能发出一个简单的get请求(虽然我想我应该写一些测试来确保没有登录就没有任何作用 - 我会去以后)。

我已经建立了一个RSpec,Capybara,FactoryGirl和Guard的测试环境(不知道使用哪种工具,所以使用Railscasts的建议)。到目前为止我编写测试的方法是在FactoryGirl中创建一个用户,就像这样;

FactoryGirl.define do
  sequence(:email) {|n| "user#{n}@example.com"}
  sequence(:login) {|n| "user#{n}"}
  factory :user do
    email {FactoryGirl.generate :email}
    login {FactoryGirl.generate :login}
    password "abc"
    admin false
    first_name "Bob"
    last_name "Bobson"
  end
end

然后像我这样写我的测试;

require 'spec_helper'

describe ReportsController do
  describe "GET 'index'" do
    it "should be successful" do
      user = Factory(:user)
      visit login_path
      fill_in "login", :with => user.login
      fill_in "password", :with => user.password
      click_button "Log in"
      get 'index'
      response.should be_success
    end
  end
end

这样就失败了;

  1) ReportsController GET 'index' should be successful
     Failure/Error: response.should be_success
       expected success? to return true, got false
     # ./spec/controllers/reports_controller_spec.rb:13:in `block (3 levels) in <top (required)>'

有趣的是,如果我将测试更改为response.should be_redirect,那么测试通过会告诉我一切正常,直到该点为止,但登录信息无法识别。

所以我的问题是我需要做些什么来使这个登录工作。我是否需要在数据库中创建与FactoryGirl凭据匹配的用户?如果是这样,FactoryGirl在这里有什么意义(我应该使用它吗?)如何在测试环境中创建这个假用户?我的身份验证系统非常简单(基于Railscasts episode 250)。这种登录行为可能必须复制几乎所有我的测试,所以如何在我的代码中执行一次并将其应用到任何地方?

我意识到这是一个很大的问题,所以我感谢你一看。

5 个答案:

答案 0 :(得分:42)

答案取决于您的身份验证实施。通常,当用户登录时,您将设置会话变量以记住该用户,例如session[:user_id]。如果不存在此类会话变量,您的控制器将检查before_filter中的登录并重定向。我假设你已经在做这样的事了。

要在测试中使用此功能,您必须手动将用户信息插入到会话中。以下是我们在工作中使用的部分内容:

# spec/support/spec_test_helper.rb
module SpecTestHelper   
  def login_admin
    login(:admin)
  end

  def login(user)
    user = User.where(:login => user.to_s).first if user.is_a?(Symbol)
    request.session[:user] = user.id
  end

  def current_user
    User.find(request.session[:user])
  end
end

# spec/spec_helper.rb
RSpec.configure do |config|
  config.include SpecTestHelper, :type => :controller
end

现在,在我们的任何控制器示例中,我们都可以调用login(some_user)来模拟以该用户身份登录。


我还应该提一下,看起来你在这个控制器测试中正在进行集成测试。通常,您的控制器测试应该只模拟对各个控制器操作的请求,例如:

it 'should be successful' do
  get :index
  response.should be_success
end

这专门测试单个控制器操作,这是您在一组控制器测试中所需的操作。然后,您可以使用Capybara / Cucumber进行表单,视图和控制器的端到端集成测试。

答案 1 :(得分:8)

spec / support / controller_helpers.rb 中添加帮助文件并复制下面的内容

module ControllerHelpers
    def sign_in(user)
      if user.nil?
        allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})
        allow(controller).to receive(:current_user).and_return(nil)
      else
        allow(request.env['warden']).to receive(:authenticate!).and_return(user)
        allow(controller).to receive(:current_user).and_return(user)
      end
    end
  end

现在在 spec / rails_helper.rb spec / spec_helper.rb 中添加以下行  文件

require 'support/controller_helpers'

RSpec.configure do |config|

    config.include Devise::TestHelpers, :type => :controller
    config.include ControllerHelpers, :type => :controller

  end

现在在您的控制器规范文件中。

describe  "GET #index" do

    before :each do        
        @user=create(:user)
        sign_in @user
    end
      ...
end

Devise Official Link

答案 2 :(得分:2)

由于我无法让@ Brandan的回答工作,但基于它和this post,我已经找到了这个解决方案:

# spec/support/rails_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # Add this at top of file

...

include ControllerMacros # Add at bottom of file

# spec/support/controller_macros.rb
module ControllerMacros

  def login_as_admin
    admin = FactoryGirl.create(:user_admin)
    login_as(admin)
  end

  def login_as(user)
    request.session[:user_id] = user.id
  end

end

然后在测试中你可以使用:

it "works" do
  login_as(FactoryGirl.create(:user))
  expect(request.session[:user_id]).not_to be_nil
end

答案 3 :(得分:0)

使用用户登录功能测试的最简单方法是使用Warden的helper #login_as

login_as some_user

答案 4 :(得分:0)

对于那些不使用 Devise 的人:

spec/rails_helper.rb

require_relative "support/login_helpers"

RSpec.configure do |config|
  config.include LoginHelpers
end

spec/support/login_helpers.rb

module LoginHelpers
  def login_as(user)
    post "/session", params: { session: { email: user.email, password: "password" } }
  end
end

在规范中:

login_as(user)