"uncaught throw :warden" when testing redirects for users not logged in
Asked Answered
N

3

8

This is a spec for a very typical controller with a before_filter which redirects to login page when a not-logged-in user (a.k.a. a guest) tries to access /projects/new.

describe ProjectsController do
  (...)

  describe "GET new" do
    context 'when not logged in' do
      before { sign_in_nobody }

      context 'creating project' do
        before { get :new }

        it 'denies access' do
          expect(response).to be_redirect
        end
      end
    end
  end
end

I have specced all possible outcomes of accessing :index, :show and :new for guests, users, admins and superadmins. I didn't have any problems using neither logged or guest users, admins or not – but this is actually the first time this spec touched an action that involves Devise's before_filter :autheticate_user! and it fails miserably at that.

As you might already suspect – the spec doesn't even reach expect(response).to be_redirect, it throws a hissy fit before:

Failures:

1) ProjectsController GET new when not logged in creating project denies access
   Failure/Error: get :new
   ArgumentError:
     uncaught throw :warden
   # ./spec/controllers/projects_controller_spec.rb:344:in `block (5 levels) in <top (required)>'

I've searched for an answer to question: "how to properly test for unauthenticated (guest) users with rspec and devise", but everyone only talks about having problem with actually logging users in devise for RSpec to use. A problem which I solved thusly:

#spec/support/devise_authenticators.rb
include Devise::TestHelpers

def sign_in_nobody
  @request.env["devise.mapping"] = Devise.mappings[:user]
  sign_in User.new
end

def sign_in_user
  @request.env["devise.mapping"] = Devise.mappings[:user]
  sign_in FactoryGirl.create(:user)
end

However it's not the logging in that fails here, it's the "being not logged". So far I got absolutely nothing and I know people HAVE to test those scenarios somehow.

Right now I'm using a walkaround:

before { sign_in_nobody }

context 'creating project' do
  it 'denies access' do
    expect{ get :new }.to raise_exception("uncaught throw :warden")
  end
end

But even tho it's practically correct (uncaught throw :warden happens only when authenticate_user! fails so it can be used as an expectation) in theory it feels really dirty.

Any ideas how to do it properly?

(...Maybe I shouldn't be testing this at all, seeing as how the correctness of before_filter authenticate_user! is Devise's responsibility not mine?)

No answered 28/10, 2013 at 1:3 Comment(1)
This is a duplicate of SO11152671. I've pasted a full answer there. In short, you'll need to add includes for the Devise test helper and the Warden test helper both in the actual ControllerTest class and not in test_helper.rb.Softcover
E
4

It should just work. I just spent a while fixing the same problem, and it turned out we had a test helper that overrides process for controller tests. Devise also overrides it to catch :warden so I just needed to include Devise::TestHelpers before including the default params module.

For an example of what I'm talking about when I say we overrode process, see this answer which describes how to set default params to include format: json

Entrench answered 5/11, 2013 at 20:46 Comment(3)
I have the same error, but I don't understand where should I insert include Devise::TestHelpers. Can you help me? ThanksAthenaathenaeum
You can do it in your test class or in the helper for all controllers. For example, see the top 2 headings on github.com/plataformatec/devise/wiki/… for Test::Unit and RSpec.Entrench
Thank you! I was having this problem where the uncaught throw :warden error was being raised, even though we had the proper test helpers included. Turned out we had a default_params helper just like the one you linked to, which was causing the problem.Tarentarentum
D
1

So the lesson is that uncaught throw :warden happens when authenticate_user! fails.

So figure out why your the user's authentication is a failing, and you'll have solved your problem.

Diplomatics answered 23/2, 2016 at 13:14 Comment(0)
R
1

I had a similar problem and I solved it like this:

e = catch(:warden) { get root_url }
expect(e[:message]).to be(:not_activated)
Remonstrate answered 6/2, 2017 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.