如何使用RSpec和Devise / CanCan进行集成测试?

时间:2011-05-03 05:48:42

标签: ruby-on-rails rspec devise cancan

如果我有一个Devise模型User,其中只允许具有role:admin的用户查看某个url,我该如何编写RSpec集成测试来检查该url的状态是否返回200?

def login(user)
  post user_session_path, :email => user.email, :password => 'password'
end

在这个问题的答案中,这是伪建议:Stubbing authentication in request spec,但我不能为我的生活让它与设计一起工作。 CanCan在检查Ability时收到一个nil用户,它自然没有正确的权限。

在集成规范中无法访问控制器,因此我无法存根current_user,但我想做类似的事情。

describe "GET /users" do
  it "should be able to get" do
    clear_users_and_add_admin #does what it says...
    login(admin)
    get users_path
    response.status.should be(200)
  end
end

注意!!! :自提出问题以来,这一切都发生了变化。目前最好的方法是:http://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara

6 个答案:

答案 0 :(得分:47)

@ pschuegr自己的回答让我跨过了界限。为了完整起见,这就是我所做的,它让我轻松设置了请求规范和控制器规范(使用FactoryGirl创建用户实例):

#module for helping controller specs
module ValidUserHelper
  def signed_in_as_a_valid_user
    @user ||= FactoryGirl.create :user
    sign_in @user # method from devise:TestHelpers
  end
end

# module for helping request specs
module ValidUserRequestHelper

  # for use in request specs
  def sign_in_as_a_valid_user
    @user ||= FactoryGirl.create :user
    post_via_redirect user_session_path, 'user[email]' => @user.email, 'user[password]' => @user.password
  end
end

RSpec.configure do |config|
  config.include ValidUserHelper, :type => :controller
  config.include ValidUserRequestHelper, :type => :request
end

然后在请求规范中:

describe "GET /things" do
  it "test access to things, works with a signed in user" do
    sign_in_as_a_valid_user
    get things_path
    response.status.should be(200)
  end
end

describe "GET /things" do
  it "test access to things, does not work without a signed in user" do
    get things_path
    response.status.should be(302) # redirect to sign in page
  end
end

并且类似地,在控制器规范中使用'signed_in_as_valid_user'(使用FactoryGirl中的用户包装Devise :: TestHelpers sign_in方法)

答案 1 :(得分:28)

啊,太近了。这样做的伎俩 - 我错过了正确的参数形式,以及重定向。

post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password

答案 2 :(得分:20)

我使用了一种稍微不同的方法,使用Warden :: Test :: Helpers。

在我的spec / support / macros.rb中,我添加了:

module RequestMacros
  include Warden::Test::Helpers

  # for use in request specs
  def sign_in_as_a_user
    @user ||= FactoryGirl.create :confirmed_user
    login_as @user
  end
end

然后将其包含在RSpec的spec_helper.rb配置中:

RSpec.configure do |config|
  config.include RequestMacros, :type => :request
end

然后在请求规范中:

describe "index" do
  it "redirects to home page" do
    sign_in_as_a_user 
    visit "/url"
    page.should_not have_content 'content'
  end
end

post_via_redirect user_session_path方法相比,这实际上有效,并允许我在before_filters中使用current_user。

答案 3 :(得分:0)

您可以创建一个宏(/spec/support/controller_macros.rb)并编写如下内容:

module ControllerMacros
  def login_user
    before(:each) do
      @request.env["devise.mapping"] = :user
      @user = Factory(:user)
      sign_in @user
    end
  end
end

您还可以包含所需的任何CanCan属性。然后,在您的规范中:

describe YourController do
    login_user

    it "should ..." do

    end

答案 4 :(得分:0)

答案 5 :(得分:0)

截至2017年中期,在我看来,我们还有一个更好的机会来整合我们的Rspecs设计。我们可以利用如下所述定义的辅助方法sign in来利用存根验证:

module ControllerHelpers
    def sign_in(user = double('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_helper.rbrails_helper.rb中附加新创建的文件:

require 'support/controller_helpers'
  ...
  RSpec.configure do |config|
    ...
    config.include Devise::TestHelpers, :type => :controller
    config.include ControllerHelpers, :type => :controller
    ...
  end

然后在方法中放置方法体sign_in的开头,以获取经过身份验证的用户的上下文,并且您已完成设置。可以在设计文档here

中找到最新的详细信息