"undefined method `env' for nil:NilClass" in 'setup_controller_for_warden' error when testing Devise using Rspec
Asked Answered
N

10

47

I'm trying to create a spec for a sign out flow by using factorygirl to create a user and then use Devise's sign_in method to authenticate the user, then use capybara to click the "Sign Out" link.

I'm getting (what seems to me to be) a strange error when I run the spec:

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

Here's the spec:

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

And my user.rb factory:

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

The error seems to be triggered simply from the line include Devise::TestHelpers, as I've tried commenting out the entire content of the spec and still get the same error.

I thought the Devise test helpers would work out of the box; did I miss some configuration? Thanks.

Nolitta answered 4/12, 2014 at 1:17 Comment(0)
S
24

Apparently there are issues with Devise::TestHelpers and integration testing, so perhaps that's the problem here.

https://github.com/plataformatec/devise (mentioned in README, Issues, etc.; also see related SO questions):

These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session;

Sidero answered 4/12, 2014 at 2:35 Comment(2)
Yeah, I think that's what's going on because including Devise::TestHelpers in a controller spec doesn't produce an error in my app. It must throw this error when included in a feature spec. Thanks.Nolitta
Guys I am looking all over for some help on this, can you share some helpful links / insights?Bagman
I
58

In Rails 5 you must include Devise::Test::IntegrationHelpers instead Devise::Test::ControllerHelpers:

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

See more:

Ignoramus answered 14/12, 2016 at 16:0 Comment(3)
This works with request specs as well if you just change the type to :requestAutomate
I don't have a rails_helper.rb, but I included it in my test, without the type: :feature and it worked perfectly. this was a tough one to track down. thank youMckellar
spent 4 hours trying to find out what am i doing wrong and finally this helped. thanksKneehole
S
24

Apparently there are issues with Devise::TestHelpers and integration testing, so perhaps that's the problem here.

https://github.com/plataformatec/devise (mentioned in README, Issues, etc.; also see related SO questions):

These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session;

Sidero answered 4/12, 2014 at 2:35 Comment(2)
Yeah, I think that's what's going on because including Devise::TestHelpers in a controller spec doesn't produce an error in my app. It must throw this error when included in a feature spec. Thanks.Nolitta
Guys I am looking all over for some help on this, can you share some helpful links / insights?Bagman
L
19

FWIW it seems like the issues have been fixed, however I ran into the issue after not reading the documentation well enough.

This was our code:

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

This means every test will include the test helpers, including models. This wound up being the issue for us. Should we have read the documentation closer we would have noticed Devise suggests limiting it to only controllers with:

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

This solved the issue for us. All tests passing :)

Loquitur answered 15/5, 2015 at 12:48 Comment(0)
C
12

Here's my solution:

class ActiveSupport::TestCase
  # all the normal stuff
end

class ActionController::TestCase
  include Devise::TestHelpers    
end
Concentric answered 5/6, 2015 at 18:41 Comment(1)
For those of us reading this that are novice, can you expand...?Pledge
W
9

I meet the same error on rails 5. Here's my solution

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
Weinstein answered 1/7, 2016 at 4:12 Comment(1)
config.include Devise::TestHelpers, type: :controller produces warning and should be config.include Devise::Test::ControllerHelpersEmmi
B
7

Like others have already said, you're including the Devise::TestHelpers. That's for testing controllers. If you'd still like to automatically login a test user in your integration tests, check out the official Devise Instructions on using it with Capybara.


Using Devise with Capybara

Basically, what you need to do is first enable Warden's test mode:

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

Then, (create and) login your user:

user = FactoryGirl.create(:user)
login_as(user, scope: :user)

Example:

# 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
Basilica answered 12/1, 2016 at 8:36 Comment(2)
This is exactly what I needed to test routes protected with Devise authentication using rspec. Thank you.Sub
Thank you, works fine, but I recommend put it on rails_helper to follow a DRY approach better.Sputum
A
3

I was having this problem when trying to sign_in a user in a before hook:

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

Placing sign_in inside the before hook leads to:

Failure/Error: sign_in user

 NoMethodError:
   undefined method `env' for nil:NilClass

But placing it inside an example works fine:

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

But this can be improved, placing the sign_in into a before(:example) that will also work:

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
Avert answered 3/12, 2016 at 2:46 Comment(0)
R
2

My Devise version is 4.2.0 so I just included

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

in my rails helper file.

Alternatively you can use the same in your spec as

include Devise::Test::ControllerHelpers
Ricky answered 22/7, 2016 at 18:18 Comment(0)
E
2

The correct syntax for Rails 5 / Devise (4.2.0) is

RSpec.configure do |config|
  config.include Devise::Test::ControllerHelpers, :type => :controller
end
  • Devise::TestHelpers are deprecated so use Devise::Test::ControllerHelpers
  • :type => :controller - to limit only for controllers and not models for example.
Emmi answered 26/7, 2016 at 11:18 Comment(0)
A
2

For the sake of being complete, with Rails 5 and RSpec I have run into similar issues when using the latest helpers, as they need to be set explicitly with the type when not used as a superclass.

So if you find yourself receiving there errors in your model tests there's a pretty good chance the type is not set.

Here's what I use in the spec_helper:

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

I know that the docs do mention this, but there are times when you may run across an older blog that gives you an older approach, or upgrading from an older setup, and next thing you know this happens.

Arabian answered 24/9, 2017 at 3:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.