-
Notifications
You must be signed in to change notification settings - Fork 0
How To: Controllers tests with Rails 3 (and rspec)
First, be sure to speed up your tests!
To sign up as admin for a given test case, just do:
class SomeControllerTest < ActionController::TestCase
include Devise::TestHelpers
def setup
@request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin)
end
endNote: If you are using the confirmable module, you should set a
confirmed_at date inside the Factory or callconfirm! before sign_in
Controller specs won’t work out of the box if you’re using any of devise’s utility methods.
As of rspec-rails-2.0.0 and devise-1.1, the best way to put devise in your specs is simply to add the following into spec_helper:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
endI also like to write controller_macros.rb inside spec/support file which contains the following:
module ControllerMacros
def login_admin
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin) # Using factory girl as an example
end
end
def login_user
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in user
end
end
endThen in spec_helper:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
endSo now in my controller specs I can now do:
describe MyController do
login_admin
it "should have a current_user" do
# note the fact that I removed the "validate_session" parameter if this was a scaffold-generated controller
subject.current_user.should_not be_nil
end
it "should get index" do
# Note, rails 3.x scaffolding may add lines like get :index, {}, valid_session
# the valid_session overrides the devise login. Remove the valid_session from your specs
get 'index'
response.should be_success
end
endEvery time you want to unit test a devise controller, you need to tell Devise which mapping to use. We need that because ActionController::TestCase and spec/controllers bypass the router and it is the router that tells Devise which resource is currently being accessed, you can do that with:
@request.env["devise.mapping"] = Devise.mappings[:admin]If you choose to authenticate in routes.rb, you lose the ability to test your routes via assert_routing (which combines assert_recognizes and assert_generates, so you lose them also). It’s a limitation in Rails: Rack runs first and checks your routing information but since functional/controller tests run at the controller level, you cannot provide authentication information to Rack which means request.env[‘warden’] is nil and Devise generates one of the following errors:
NoMethodError: undefined method 'authenticate!' for nil:NilClass
NoMethodError: undefined method 'authenticate?' for nil:NilClassThe solution is to test your routes via integration tests. Authenticating your routes has its advantages, this is one of the disadvantages.
If you’re using the default Rspec scaffold generator, the generated controller specs pass along session parameters:
get :index, {}, valid_sessionThese are overwriting the session variables that Devise’s helpers set to sign in with Warden. The simplest solution is to remove them:
get :index, {}Alternatively, you could set the Warden session information in them manually, instead of using Devise’s helpers.
credit: Ian Terrell’s answer on stackoverflow