How to do integration testing with RSpec and Devise/CanCan?
Asked Answered
D

6

37

If I have a Devise model User, of which only those users with role :admin are allowed to view a certain url, how can I write an RSpec integration test to check that the status returns 200 for that url?

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

This was pseudo-suggested in the answer to this question: Stubbing authentication in request spec, but I can't for the life of me get it to work with devise. CanCan is receiving a nil User when checking Ability, which doesn't have the correct permissions, naturally.

There's no access to the controller in integration specs, so I can't stub current_user, but I'd like to do something like this.

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

NOTE!!!: all this has changed since the question was asked. The current best way to do this is here: http://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara

Dorinedorion answered 3/5, 2011 at 5:48 Comment(1)
Note: all this has changed since the question was asked. The current best way to do this is here: github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara Just to reiterate for first sigh readers like meFitting
L
48

@pschuegr's own answer got me across the line. For completeness, this is what I did that gets me easily set up for both request specs and controller specs (using FactoryGirl for creating the user instance):

in /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

Then in request spec:

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

and similarly, use 'signed_in_as_valid_user' in controller specs (which wraps Devise::TestHelpers sign_in method with a user from FactoryGirl)

Leaving answered 30/1, 2012 at 9:48 Comment(5)
I have defined the same modules as you mentioned, but I get this error ` undefined local variable or method sign_in_as_a_valid_user' for #<Class:0x0000000559baa0> (NameError). Where am I making a mistake?Cowlick
This is in a request spec, right? Is your Rspec.configure code executing?Leaving
Yes, this is in a request spec. How do I check whether the Rspec.configure code executes?Cowlick
I'd put a breakpoint there if you can run your specs through an IDE with a debugger (like Rubymine) or try the pry gem, or a good ol' puts "hello world" in there :)Leaving
@MattConnolly, can you modify your answer to have require 'spec_helper' at the top of your request spec?Mezzorilievo
D
28

Ah, so close. This does the trick - I was missing the proper parameter form, and the redirecting.

post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
Dorinedorion answered 3/5, 2011 at 17:15 Comment(4)
I had to use page.driver.post instead of post_via_redirect (request specs)Sarnoff
hey how can i user current_user in request spec test cases like how we use in controller spec test casesAlly
I got the error undefined local variable or method 'user_session_path'Twirp
@Twirp For future references: You can use Rails.application.routes.url_helpers.user_session_path or include Rails.application.routes.url_helpers beforehand if you use this in place where url_helpers not accessible. But they should be included in tests by default though, as far as I know.Comptometer
B
21

I used a slightly different approach, using the Warden::Test::Helpers.

In my spec/support/macros.rb I added:

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

And then included that in RSpec's config in spec_helper.rb:

RSpec.configure do |config|
  config.include RequestMacros, :type => :request
end

And then in the request specs themselves:

describe "index" do
  it "redirects to home page" do
    sign_in_as_a_user 
    visit "/url"
    page.should_not have_content 'content'
  end
end

In contrast to the post_via_redirect user_session_path method, this actually works and allows me to use current_user in before_filters, for example.

Bernardobernarr answered 10/8, 2012 at 13:27 Comment(1)
Wiki article: github.com/plataformatec/devise/wiki/How-To:-Test-with-CapybaraRakel
F
0

You can create a macro (/spec/support/controller_macros.rb) and write something like :

module ControllerMacros
  def login_user
    before(:each) do
      @request.env["devise.mapping"] = :user
      @user = Factory(:user)
      sign_in @user
    end
  end
end

You can also include any CanCan attributes you want. Then, in your spec :

describe YourController do
    login_user

    it "should ..." do

    end
Fiora answered 3/5, 2011 at 5:59 Comment(3)
Thanks for your comment Spyros, but it works fine in the controller using the Devise::TestHelpers. It's just in the request specs it doesn't work (@request is nil).Dorinedorion
You mention that you receive a nil user. This would mean that the spec does not login the user. Are you sure you use devise helpers and login correctly ? You can test that inside your spec to make sure. If not, you can try the code above. I think it all comes from the fact that your spec does not log in a user.Fiora
This answer is for controller testing, not integration testing, like the question was for - the comments about the nil user submmited by the asker is because the answer is for the wrong situationBord
C
0

I found this link very helpful https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)

Chandigarh answered 2/11, 2014 at 12:25 Comment(0)
T
0

As of mid 2017, we have one more, in my opinion better opprotunity to integrate devise in our Rspecs. We are able to utilize stub authentization with helper method sign in defined as described below:

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

You should also append in spec_helper.rb or rails_helper.rb reference to newly created file:

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

Then in method just place at the beginning of the method body sign_in for context for authenticated user and you are all set. Up to date details can be found in devise docs here

Tauromachy answered 19/5, 2017 at 13:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.