How to do integration testing with RSpec and Devise/CanCan?
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 so开发者_如何学编程mething 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
@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)
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
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.
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
I found this link very helpful https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)
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
精彩评论