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


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

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

在这个问题的答案中,这是伪建议的: 在请求规范中对验证进行验证,但我不能为我的生活让它与设计一起工作。 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


36
2018-05-03 05:48


起源


注意:自提出问题以来,这一切都发生了变化。目前最好的方法是: github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara 只是重申像我这样的第一个叹息读者 - Jahan Zinedine


答案:


@ 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'(它与来自FactoryGirl的用户包装Devise :: TestHelpers sign_in方法)


47
2018-01-30 09:48



我已经定义了与你提到的相同的模块,但是我得到了这个错误`未定义的局部变量或方法 sign_in_as_a_valid_user' for #<Class:0x0000000559baa0> (NameError)。我在哪里弄错了? - rohitmishra
这是一个请求规范,对吗?是你的 Rspec.configure 代码执行? - Matt Connolly
是的,这是一个请求规范。我该如何检查 Rspec.configure 代码执行? - rohitmishra
如果您可以通过带有调试器的IDE(如Rubymine)运行您的规范,或者尝试使用 pry 宝石,还是一个好的'' puts "hello world" 在那里 :) - Matt Connolly
@MattConnolly,你可以修改你的答案吗? require 'spec_helper' 在您的请求规范的顶部? - Purplejacket


啊,太近了。这样做的技巧 - 我错过了正确的参数形式和重定向。

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

28
2018-05-03 17:15



正是我在寻找的东西。谢谢!˚F - John
我不得不使用 page.driver.post 代替 post_via_redirect (要求规格) - Michael De Silva
嘿我如何在请求规范测试案例中使用current_user,例如我们如何在控制器规范测试用例中使用 - sharath
我收到了错误 undefined local variable or method 'user_session_path' - Phifo
@Phifo供将来参考:您可以使用 Rails.application.routes.url_helpers.user_session_path    要么 include Rails.application.routes.url_helpers 事先如果你在url_helpers无法访问的地方使用它。但据我所知,它们应默认包含在测试中。 - Kirill Fedyanin


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


20
2017-08-10 13:27



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


您可以创建一个宏(/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

0
2018-05-03 05:59



感谢您对Spyros的评论,但它在使用Devise :: TestHelpers的控制器中工作正常。它只是在请求规范中它不起作用(@request是零)。 - pschuegr
你提到你收到一个零用户。这意味着规范不会登录用户。您确定使用设计助手并正确登录吗?您可以在规范中对其进行测试以确保。如果没有,您可以尝试上面的代码。我认为这一切都来自于您的规范未登录用户的事实。 - Spyros
这个答案是针对控制器测试而不是集成测试,就像问题所针对的那样 - 对于提问者提交的nil用户的评论是因为答案是针对错误的情况 - Houen


我发现此链接非常有用 https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)


0
2017-11-02 12:25





截至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.rb 要么 rails_helper.rb 对新创建的文件的引用:

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

然后在方法中放置在方法体的开头 sign_in 对于经过身份验证的用户的上下文,您已完成设置。最新的详细信息可以在设计文档中找到 这里


0
2018-05-19 13:10