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

37

如果我有一个使用Devise认证的用户模型User,只有那些拥有角色:admin的用户可以查看某个url,我该如何编写RSpec集成测试来检查该url的状态是否为200?

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

这在回答请求规范中存根认证问题时似乎被建议过,但我无论如何都无法让它与 devise 一起工作。当检查权限时,CanCan 收到了一个空的用户,该用户没有正确的权限。

在集成规范中无法访问控制器,因此我无法存根 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


2
请注意:自提问以来,所有这些都已经改变。目前最好的方法在这里:http://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara。只是为了再次强调,对于像我这样的初学者。 - Jahan Zinedine
6个回答

48

@pschuegr的回答帮助我解决了问题。为了完整起见,这是我所做的,让我轻松地为请求规范和控制器规范进行设置(使用FactoryGirl创建用户实例):

在/spec/support/sign_in_support.rb文件中:

#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'(它将Devise :: TestHelpers的sign_in方法与由FactoryGirl创建的用户结合在一起)。


我已经按照你提到的定义了相同的模块,但是我遇到了这个错误 undefined local variable or method 'sign_in_as_a_valid_user' for #<Class:0x0000000559baa0> (NameError)。我错在哪里了? - rohitmishra
这是在请求spec中,对吗?你的 Rspec.configure 代码是否正在执行? - Matt Connolly
是的,这是一个请求规范。我如何检查Rspec.configure代码是否执行? - rohitmishra
如果您可以通过带有调试器的IDE(例如Rubymine)运行您的规范测试,那么我建议您在那里设置一个断点。或者,您也可以尝试使用pry gem或一个简单的puts“hello world”语句来进行调试 :) - Matt Connolly
@MattConnolly,你能否修改你的答案,在你的请求规范的顶部加上 require 'spec_helper' - Purplejacket

28

啊,就快了。这个方法就可以了 - 我漏掉了正确的参数形式和重定向。

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

正是我所需要的。谢谢! - John
4
在请求规范(request specs)中,我不得不使用page.driver.post代替post_via_redirect - Michael De Silva
2
嘿,我该如何在请求规范测试用例中使用current_user,就像我们在控制器规范测试用例中使用的那样? - sharath
2
我遇到了错误 undefined local variable or method 'user_session_path' - Phifo
@Phifo 供以后参考:如果您在无法访问url_helpers的地方使用它,可以使用Rails.application.routes.url_helpers.user_session_path或者事先包含include Rails.application.routes.url_helpers。但是据我所知,它们应该默认包含在测试中。 - Kirill Fedyanin

21

我使用了稍微不同的方法,使用了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

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

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。


维基文章:https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara - denis.peplin

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

3
谢谢您的留言,Spyros。但是在使用Devise::TestHelpers控制器中它能正常工作。只是在请求规范(request specs)中它无法工作(@request为空)。 - psugar
你提到你收到了一个空的用户。这意味着规范没有登录用户。你确定你使用了devise助手并正确登录了吗?你可以在规范中进行测试以确保。如果没有,你可以尝试上面的代码。我认为这一切都源于你的规范没有登录用户。 - Spyros
2
这个答案是针对控制器测试而不是集成测试的,就像问题所问的那样 - 有关请求者提交的nil用户的评论是因为答案适用于错误的情况。 - Houen


0
到了2017年中期,我们有一个更好的机会去在RSpecs中整合设备(devise)。我们可以利用带有帮助方法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以提供已认证用户的上下文,您就可以开始了。有关最新详细信息,请参阅devise文档此处


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接