2

Devise adds the #authenticated and #unauthenticated constraints to the routing dsl but they rely on request.env['warden'] responding to #authenticate?. As I understand it, the request doesn't exist in a routing spec so this fails with:

# NoMethodError:
#   undefined method `authenticate?' for nil:NilClass

Devise issues #1670, #1779 suggest that this is a limitation but don't suggest how to overcome it.

I decided to try to just stub out authenticate? but have never done stubbing before so this has been mostly hit-or-miss.

My first attempt looks like this:

require 'spec_helper'

describe 'routes for Tenant' do
  it 'routes /organization to TenantsController#show' do
    allow_message_expectations_on_nil
    allow_any_instance_of(Object).to receive(:authenticate?).and_return(true)

    expect(get: '/organization').to route_to(
      controller: 'tenants',
      action:     'show'
    )
  end
end

The question: I'm not comfortable stubbing authenticate? globally nor on NilClass. How can I stub this ... more generally ... so that I'm not cornered into a specific implementation?

I would like to somehow stub out #authenticated and #unauthenticated to simply ignore their implementation and yield but attempting to do that still results in the original NoMethodError as if my fake implementation doesn't exist:

expect_any_instance_of(ActionDispatch::Routing::Mapper).to receive(:authenticated) { yield }
expect_any_instance_of(ActionDispatch::Routing::Mapper).to receive(:unauthenticated) { false }

Why does it work the first way but not the second?

  • Why not just describe this in a controller spec? Part of the remit of controller specs is to test that redirects are happening as they should. I could be wrong but I think the remit of routing specs is more for ensuring that *publicly available* routes are available and correct, and that URLs which map to private routes are unroutable. – Stefan Magnuson Feb 07 '14 at 00:59
  • I understand what you're saying and the example I gave isn't a particularly good candidate for routing specs. I have actually moved that test into my controller spec. But the same issue still holds true for a handful of public routes that are defined inside of an `#unauthenticated` block. At this point, I'm just anxious to figure out why it doesn't work - and how to make it work - purely to scratch the itch. I was surprised when my first attempt worked and now I'm puzzled that the second attempt seems unworkable. – Frank Joseph Mattia Feb 07 '14 at 01:32
  • I spoke too soon. Now that I've put them in my controller spec they still fail with the same error. – Frank Joseph Mattia Feb 07 '14 at 01:44
  • Yes you're right... I believe the issue is that since Rack isn't loaded at all in RSpec tests, and so no Rack middleware is either, meaning Warden isn't loaded as it is a Rack middleware. You can shoehorn Warden into your tests (http://stackoverflow.com/a/13422870/2025180) but given the complexity perhaps it is best to just `include Warden::Test::Helpers` and use them to test your application, leaving the routing specs to only confirm that private URLs are not routable in the public namespace, and then in your controller tests confirm that users are redirected to the right place. – Stefan Magnuson Feb 07 '14 at 03:19
  • Even using the route spec in that manner is impossible due my routes file using the devise methods. My first example does solve the issue but is not acceptable to me. It seems like it should be possible to ignore the implementation details of authenticated/unauthenticated in my routes file and just stub them to provide the behavior I wish to test. – Frank Joseph Mattia Feb 07 '14 at 13:18
  • I have routing specs passing for routes behind devise without stubbing anything. The only item I have is `config.include Devise::TestHelpers, :type => :controller` in my `spec_helper.rb`. If I was in a normal controller I would do a `before(:each)` and sign in my user. Can you run your initial attempt without the stub line? – jstim Feb 13 '14 at 08:40
  • I'm doing exactly that in my controller specs but even that does not allow `route_to` to work. I get the same error. – Frank Joseph Mattia Feb 13 '14 at 14:51

0 Answers0