【问题标题】:"undefined method `env' for nil:NilClass" in 'setup_controller_for_warden' error when testing Devise using Rspec使用 Rspec 测试设计时出现“setup_controller_for_warden”错误中的“nil:NilClass 的未定义方法 `env'”
【发布时间】:2014-12-04 01:17:45
【问题描述】:

我正在尝试使用 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 行触发的,因为我尝试注释掉规范的全部内容,但仍然得到相同的错误。

我认为 Devise 测试助手可以开箱即用;我错过了一些配置吗?谢谢。

【问题讨论】:

    标签: ruby-on-rails-4 rspec devise capybara factory-bot


    【解决方案1】:

    在 Rails 5 中,您必须包含 Devise::Test::IntegrationHelpers 而不是 Devise::Test::ControllerHelpers

    # rails_helper.rb
    config.include Devise::Test::IntegrationHelpers, type: :feature
    

    查看更多:

    【讨论】:

    • 如果您只是将类型更改为:request,这也适用于请求规范
    • 我没有 rails_helper.rb,但我将它包含在我的测试中,没有 type: :feature 并且它运行良好。这是一个很难追踪的问题。谢谢
    • 花了 4 个小时试图找出我做错了什么,最后这有所帮助。谢谢
    【解决方案2】:

    显然Devise::TestHelpers 和集成测试存在问题,所以也许这就是问题所在。

    https://github.com/plataformatec/devise(在 README、Issue 等中提到;另见相关 SO 问题):

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

    【讨论】:

    • 是的,我认为这是正在发生的事情,因为在控制器规范中包含 Devise::TestHelpers 不会在我的应用程序中产生错误。当包含在功能规范中时,它必须抛出此错误。谢谢。
    • 伙计们,我正在四处寻求帮助,您能分享一些有用的链接/见解吗?
    【解决方案3】:

    FWIW 似乎问题已得到解决,但是我在没有充分阅读文档后遇到了问题。

    这是我们的代码:

    RSpec.configure do |config|
      ...  
      config.include Devise::TestHelpers
      ...
    end
    

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

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

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

    【讨论】:

      【解决方案4】:

      这是我的解决方案:

      class ActiveSupport::TestCase
        # all the normal stuff
      end
      
      class ActionController::TestCase
        include Devise::TestHelpers    
      end
      

      【讨论】:

      • 对于我们这些阅读本文的新手,您能扩展...吗?
      【解决方案5】:

      我在 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 焦点

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

      【讨论】:

      • config.include Devise::TestHelpers, type: :controller 产生警告并且应该是 config.include Devise::Test::ControllerHelpers
      【解决方案6】:

      就像其他人已经说过的那样,您包括Devise::TestHelpers。那是给testing controllers的。如果您仍想在集成测试中自动登录测试用户,请查看官方 Devise Instructions on using it with Capybara


      将设计与 Capybara 一起使用

      基本上,你需要先开启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 测试受设计身份验证保护的路由。谢谢。
      • 谢谢,工作正常,但我建议将它放在 rails_helper 上以更好地遵循 DRY 方法。
      【解决方案7】:

      我在尝试 sign_in 前钩子中的用户时遇到了这个问题:

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

      sign_in 放在 before 钩子中会导致:

      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
      

      【讨论】:

        【解决方案8】:

        我的 Devise 版本是 4.2.0,所以我只包含了

        config.include Devise::Test::ControllerHelpers, type: :controller
        

        在我的 rails 帮助文件中。

        或者,您可以在规范中使用与

        相同的内容
        include Devise::Test::ControllerHelpers
        

        【讨论】:

          【解决方案9】:

          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 - 仅限于 例如,控制器而不是模型。

          【讨论】:

            【解决方案10】:

            为了完整起见,在使用 Rails 5 和 RSpec 时,我在使用最新的帮助器时遇到了类似的问题,因为它们需要在不用作超类时显式设置类型。

            因此,如果您发现自己在模型测试中收到错误,则很有可能未设置类型。

            这是我在 spec_helper 中使用的:

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

            我知道文档确实提到了这一点,但有时您可能会遇到一个旧博客,它为您提供了一个旧方法,或者从旧设置升级,然后您知道接下来会发生这种情况。

            【讨论】:

              猜你喜欢
              • 2017-07-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-07-31
              • 1970-01-01
              相关资源
              最近更新 更多