Search code examples
ruby-on-railsdeviserackfunctional-testingwarden

assert_routing bypasses warden/devise setup


In my (Rails 3.2) Test::Unit controller/functional tests, assert_routing is failing with this error:

  1) Error:
test: with an admin user routing should route GET /admin/contracts to/from {:action=>"index", :controller=>"admin/contracts"}. (Admin::ContractsControllerTest):
NoMethodError: undefined method `authenticate!' for nil:NilClass

with route:

authenticate :admin do
  namespace :admin do
    resources :contracts
  end
end

I'm setting up Devise 2.0 authentication in my controller tests:

admin = Factory.create(:admin)
admin.confirm!
@request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in :admin, admin

This answer Stubbing Warden on Controller Tests indicates that rack may be authenticating even before my application runs. Which is strange, because my controller tests are running and should have already setup the env variable. But at the time authenticate is called, request.env["warden"] is nil.

Is that the case, that rack is running before the Devise helper sets up the env variable? And if so, how do I setup authentication before rack checks my routing file? My other asserts pass, but assert_routing seems to be a special case.

edit:

I verified that my setup is running before the call to #authenticate and Devise is indeed initializing request.env['warden'] with a Warden::Proxy object, but when #authenticate is called, `request.env['warden'] is nil. Does that mean that rack is running in a separate thread or something. So confusing, I'm sure I'm doing something wrong. -_-


Solution

  • Controller (functional) tests are supposed to remain at the controller level and do not have access to the lower level environment state. So request.env['warden'] is mocked in Devise::TestHelper so that controller level tests can pass, but when Rack runs the request.env hash is nil and the route fails with the above error.

    This is the beauty and the beast of authenticating in routes.rb: we don't have to worry about authentication at the controller level, but neither can we test our routes at the controller level. At least not at this point (in Rails 3.2) as it's a limitation of Rails.

    See the closed issue https://github.com/plataformatec/devise/issues/1670