在使用Rspec测试Devise时出现了“setup_controller_for_warden中未定义方法'env'的nil:NilClass”错误。

47

我正在尝试使用factorygirl创建一个用户并使用Devise的sign_in方法对用户进行身份验证,然后使用capybara点击“退出”链接来创建一个注销流程的规范。

当我运行规范时,我遇到了一个(对我来说似乎很奇怪的)错误:

Failures:

  1) Sign out flow successfully redirects to the welcome index (root)
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `env' for nil:NilClass
     # /home/vagrant/.rvm/gems/ruby-2.0.0-p576/gems/devise-3.4.1/lib/devise/test_helpers.rb:24:in `setup_controller_for_warden'

Finished in 0.00226 seconds (files took 3.32 seconds to load)
1 example, 1 failure

这是规格说明:

require 'rails_helper'

describe "Sign out flow" do

  include Devise::TestHelpers

  describe "successfully" do
    it "redirects to the welcome index (root)" do
      user = create(:user)
      sign_in user


      within '.user-info' do
        click_link 'Sign Out'
      end

      expect(current_path).to eq root_path
    end
  end
end

我的user.rb工厂:

FactoryGirl.define do
  factory :user do
    name "Fake User"
    sequence(:email, 100) { |n| "person#{n}@example.com" }
    password "helloworld"
    password_confirmation "helloworld"
    confirmed_at Time.now
  end
end

这个错误似乎仅仅是由于这一行代码 include Devise::TestHelpers 触发的,因为我已经尝试将 spec 文件中的所有内容注释掉但仍然得到相同的错误。

我原以为Devise测试助手可以直接使用;难道我错过了某些配置吗?谢谢。

10个回答

58

1
如果你将类型改为:request,那么也适用于请求规格。 - oneWorkingHeadphone
我没有rails_helper.rb文件,但我在我的测试中包含了它,没有使用type: :feature,它完美地工作了。这是一个很难追踪的问题。谢谢。 - Gambai
我花了4个小时试图找出我做错了什么,最终这个帮了我。谢谢。 - Shairyar

24

显然在使用 Devise :: TestHelpers 进行集成测试时存在问题,因此这可能是问题的原因。

https://github.com/plataformatec/devise(在README、Issues等中提到;还可以参见相关的SO问题):

这些帮助程序不适用于由Capybara或Webrat驱动的集成测试。 它们仅适用于功能测试。 相反,请填写表单或明确设置会话中的用户;


是的,我认为这就是发生的事情,因为在控制器规范中包含Devise :: TestHelpers不会在我的应用程序中产生错误。它必须在功能规范中包含时抛出此错误。谢谢。 - jpalmieri
4
各位,我正在寻求帮助,能否分享一些有用的链接/见解? - hakunin

19

就我所知,问题似乎已经得到解决,但是由于没有仔细阅读文档,我遇到了这个问题。

这是我们的代码:

RSpec.configure do |config|
  ...  
  config.include Devise::TestHelpers
  ...
end
这意味着每个测试都将包括测试助手,包括模型。这最终成为我们的问题所在。如果我们仔细阅读了文档,我们会注意到Devise建议将其限制为仅控制器:

这意味着每个测试都将包括测试助手,包括模型。这最终成为我们的问题所在。如果我们仔细阅读了文档,我们会注意到Devise建议将其限制为仅控制器:

RSpec.configure do |config|
  ...  
  config.include Devise::TestHelpers, type: :controller
  ...
end

这解决了我们的问题。所有测试都通过了 :)


12

这是我的解决方案:

class ActiveSupport::TestCase
  # all the normal stuff
end

class ActionController::TestCase
  include Devise::TestHelpers    
end

对于那些刚入门的读者,你能否详细解释一下……? - Kevin Brown

9

我遇到了与Rails 5相关的同样错误。以下是我的解决方案:

spec/rails_helper.rb

RSpec.configure do |config|
  config.include Devise::TestHelpers, type: :controller
  config.include Devise::TestHelpers, type: :view
  config.include Warden::Test::Helpers
end

spec/controllers/your_controller_spec.rb

RSpec.describe YourController, type: :controller do
  before(:all) do
  user = FactoryGirl.create(:user)
  login_as user, scope: :user
end

it "#index" do
  get "index"
  expect(response).to render_template(:index)
  expect(response).to have_http_status(200)
end

$ rspec --tag focus

Run options: include {:focus=>true}
DashboardController
#index
Finished in 3.9 seconds (files took 3.5 seconds to load)
1 example, 0 failures

1
配置包含Devise :: TestHelpers,类型:控制器会产生警告,应该是config.include Devise :: Test :: ControllerHelpers - Ivailo Bardarov

7

像其他人已经说过的那样,你正在包含Devise :: TestHelpers。 这是用于测试控制器的。 如果您仍然想在集成测试中自动登录测试用户,请查看官方使用Capybara的Devise说明


使用Capybara与Devise

基本上,您需要做的第一件事是启用Warden的测试模式:

include Warden::Test::Helpers
Warden.test_mode!

然后,创建并登录您的用户:
user = FactoryGirl.create(:user)
login_as(user, scope: :user)

例子:

# spec/features/survey_spec.rb
require 'rails_helper'

feature 'survey app' do
    include Warden::Test::Helpers

    let(:user)   { create(:user) }
    let(:survey) { create(:survey_with_questions) }

    before do
        # Sign the User in
        Warden.test_mode!
        login_as(user, scope: user)
    end

    it 'renders the survey' do
        visit survey_show_path(survey)
        expect(page).to have_content(survey.title)
    end
end

这正是我需要的,用于使用rspec测试受Devise身份验证保护的路由。谢谢。 - amatriain
谢谢,很好用,但我建议将其放在rails_helper中,以更好地遵循DRY原则。 - pmargreff

3

在 before hook 中尝试 sign_in 用户时,我遇到了这个问题:

before(:context) do
  create(:skill, name: 'Google Maps API'.downcase)
  user = create(:user)
  sign_in user
end

sign_in放在before hook中会导致以下结果:
Failure/Error: sign_in user

 NoMethodError:
   undefined method `env' for nil:NilClass

但是把它放在一个示例中可以正常工作:
shared_examples_for('an authenticated resource.') do
  describe 'An authenticated request' do
    it "responds with HTTP status OK" do
      user = create(:user)
      sign_in user
      make_request
      expect(response).to have_http_status(:ok)
    end
  end
end

但这可以进行优化,将 sign_in 放置在 before(:example) 中,这也能够正常工作:

context 'allow search by keyword' do
  let!(:skill){ create(:skill, name: 'Google Maps API'.downcase) }
  let!(:user) { create(:user) }

  before(:example) { sign_in user }

  it 'finds matching records' do
    get :search, name: "Google Maps API", format: :json
    expect(assigns(:skills).size).to be(1)
  end
  it 'finds records that start with keyword'
  it 'finds records that end with keyword'
  it 'finds records that contains keyword'
end

2

为了完整起见,在使用Rails 5和RSpec时,我遇到了类似的问题,当最新的helpers未作为超类使用时,需要显式设置类型。

因此,如果您在模型测试中遇到这些错误,很有可能类型未设置。

以下是我在spec_helper中使用的内容:

  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include Devise::Test::ControllerHelpers, type: :view
  config.include Devise::Test::IntegrationHelpers, type: :feature

我知道文档中提到了这一点,但有时您可能会遇到旧博客给出的旧方法或从旧设置升级,接下来就会发生这种情况。


2
我的 Devise 版本是 4.2.0,所以我只需包含以下内容:
config.include Devise::Test::ControllerHelpers, type: :controller

在我的Rails助手文件中。
或者,您可以在规范中使用相同的内容。
include Devise::Test::ControllerHelpers

2
Rails 5 / Devise (4.2.0) 的正确语法是:
RSpec.configure do |config|
  config.include Devise::Test::ControllerHelpers, :type => :controller
end
  • Devise::TestHelpers已经被弃用,应该使用Devise::Test::ControllerHelpers
  • :type => :controller - 仅针对控制器限制,不适用于模型。

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