drapergem / draper

Decorators/View-Models for Rails Applications
MIT License
5.23k stars 527 forks source link

Decorator spec can't access current_user when nil #857

Closed jrochkind closed 1 month ago

jrochkind commented 5 years ago

My decorators sometimes need to access the current_user? helper, from devise.

If I mock a current user using Devise::Test::ControllerHelpers sign_in, like so:

before do
  sign_in create(:user)
end

That works, no problem. My decorators that access current_user get back that specified user when run under the spec, all is good.

But my app has public facing pages that have no signed in user, current_user is nil.

If I run a spec that has not done a Devise::Test::ControllerHelpers sign_in with a user -- because I am trying to test the scenario where there is no logged_in user, current_user is nil -- and that spec tries to exersize a decorator which asks for current_user, I get the dreaded:

     Devise::MissingWarden:
       Devise could not find the `Warden::Proxy` instance on your request environment.
       Make sure that your application is loading Devise and Warden as expected and that the `Warden::Manager` middleware is present in your middleware stack.
       If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` module to inject the `request.env['warden']` object for you.

Very simple reproduction (Devise 4.6.2, Draper 3.1.0, Rails 5.2.3):

# app/decorators/test_decorator.rb
class TestDecorator < Draper::Decorator
  def has_current_user?
    h.current_user.present?
  end
end

# spec/decorators/test_decorator_spec.rb
require 'rails_helper'

describe TestDecorator do
  let(:decorator) { TestDecorator.new(Object.new) }
  it "can answer has_current_user?" do
    expect(decorator.has_current_user?).to be false
  end
end

That will raise the Devise::MissingWarden

Is there a way to set up my :decorator specs so h.current_user works, even when I am not mocking a logged in user, I want no logged in user?

Ideally it would be something I could set up globally that would Just Work, and then specs which do want a logged in user would use Devise::Test::ControllerHelpers#sign_in as ordinary. If I need to do completely different mutually exclusive set up for "a mocked logged in user" vs "no mocked logged in user, current_user should be nil", that would be unfortunate. But would still at least get my tests possible again!

jrochkind commented 5 years ago

Ah, I see the sign_in method I am using in test setup in a decorator test, that I thought was coming from Devise::Test::ControllerHelpers, is actually coming from this gem, Draper::DeviseHelper.

Not sure what's going on, or how this changes things. But looking at what that does, to see if I can figure out how to make it set up a nil current_user instead... and if there's a way to do this in a global before block for decorator specs, that can still be overrridden by a specific sign_in.

jrochkind commented 5 years ago

This seems to work if I put it in my rails_helper.rb:

  config.before(:each, type: :decorator) do
    _stub_current_scope :user, nil
  end

Can't actually use the Draper::DeviseHelper#sign_in, because of the way nil short-circuits it, so do have to use the underscored (presumably not meant for public consumption) Draper::DeviseHelpers#_stub_current_user_scope.

This will of course define a singleton method current_user on the fly.

If I then have some tests that do want a non-nil current_user, and call sign_in with an argument... that will define a current_user on the singleton again, on top of the first one.

This does seem to work... but is all a bit hacky.

Should Draper document this better and/or support this better? It seems reasonable to want devise current_user to work for nil user in decorator tests, not just for when a particular user has been mocked as logged in with sign_in.

Alexander-Senko commented 1 month ago

Did you try sign_out? That should work:

https://github.com/drapergem/draper/blob/709338469e07f58d0f05e907640e48f32ed25a60/spec/dummy/spec/decorators/devise_spec.rb#L49-L53