cancan icon indicating copy to clipboard operation
cancan copied to clipboard

current_user, or current_something_else in RSpec view specs?

Open JohnSmall opened this issue 12 years ago • 17 comments

In RSpec view specs 'can?' in the view is calling the default 'current_user', which fails because I'm not using current_user, but current_something_else.

I can see how to change the default behaviour of current_ability so that it uses something other than current_user, in my case current_member. This works just fine everywhere, except in Rspec view specs, where 'can?' is somehow calling for current_user, which doesn't exist and breaks my tests.

I'm using Devise 2.0.4, Rails 3.2.1, Rspec 2.8.0 and CanCan 1.6.7. It's proving to be quite tricky to get the combination to work together. I've mostly worked through the issues, and this might even be the last one. The issue seems to be 'Where does RSpec define the controller it uses for testing views?'. I've copied the redefinition of current_ability from ApplicationController into a module that gets included into whatever it is that RSpec invokes when it runs view specs, but it's not working.

When testing views Rspec obviously doesn't use ApplicationController or any methods I've defined in it , the error I get is;- ActionView::Template::Error: undefined local variable or method `current_user' for #ActionView::TestCase::TestController:0x00000002d8e710

So it looks like I've got to include the method re-definition of 'current_ability' into ActionView::TestCase::TestController. I could just monkey patch it in, but that feels extremely clunky. There must be an elegant way to handle this problem.

Any comments?

By the way, If you're looking for a new topic for RailsCasts, a series on the internals of RSpec and what it's doing behind the curtains would be useful. The baby steps that most blogs/instruction manuals give just aren't comprehensive enough.

Ta

John Small

JohnSmall avatar Apr 09 '12 07:04 JohnSmall

You are trying to access a helper method defined in controller for your views specs. The way to go is to do something like this:

view.stub(:current_user) { User.new # return a user }

This establishes a separation of view and controllers. However, this doesn't do a full stack testing, so you need to ensure you have integration tests. Here's a link to the rspec documentation.

andhapp avatar May 12 '12 16:05 andhapp

Unfortunately that completely misses the point. The point being that I don't have a User model. Instead I have a Member model. Even though CanCan allows you to override the default model of 'User' it seems that there's one place in the code where it's hard coded to assume you're using a User model. That shows up when using RSpec to test the views.

I think I'll have to dive into the code, fix it myself, and send you a pull request.

JohnSmall avatar May 12 '12 17:05 JohnSmall

I am intrigued to see your code and see if I can spot anything. Is it possible to share your code. Also, can you please post the full error stack?

Thanks.

On 12 May 2012, at 18:04, John [email protected] wrote:

Unfortunately that completely misses the point. The point being that I don't have a User model. Instead I have a Member model. Even though CanCan allows you to override the default model of 'User' it seems that there's one place in the code where it's hard coded to assume you're using a User model. That shows up when using RSpec to test the views.

I think I'll have to dive into the code, fix it myself, and send you a pull request.


Reply to this email directly or view it on GitHub: https://github.com/ryanb/cancan/issues/598#issuecomment-5670063

andhapp avatar May 12 '12 22:05 andhapp

I've followed the instructions for changing the default 'current_user' to something else. In my case I have a Member model instead of a User So in my application_controller I have.

def current_ability @current_ability ||= Ability.new(current_member) end

Devise provides the current_member method

In a view e.g. index.html, I want to display 'add new member' only if the current_member has rights to add a new member. So in index.html.erb I have

<%= can?(:create, Member) ? link_to('New Member', new_admin_member_path) : '' %>

Which works when I run cucumber or actually use the application. But if I try to test the view in rspec then I get this error

admin/members/index renders a list of members Failure/Error: render ActionView::Template::Error: undefined local variable or method current_user' for #<ActionView::TestCase::TestController:0x00000004859978> # ./app/views/admin/members/index.html.erb:24:inblock in _app_views_admin_members_index_html_erb__4169748593015297910_37833380' # ./app/views/admin/members/index.html.erb:15:in each' # ./app/views/admin/members/index.html.erb:15:in _app_views_admin_members_index_html_erb__4169748593015297910_37833380' # ./spec/views/admin/members/index.html.erb_spec.rb:29:in `block (2 levels) in <top (required)>'

Taking your hint that Rspec separates the controller logic from the view logic and I need to define a current member, and presumably a current_ability in the view test I added this to my spec;-

view.stub(:current_member) { Member.new }# return a member } view.stub(:current_ability){ @current_ability ||= Ability.new(current_member)}

But that fails with the same error.

So even though I've defined current_ability to use a Member model rather than a User model, it's still looking for current_user and not finding one

Thanks

John Small

On 12 May 2012 23:26, Anuj Dutta < [email protected]

wrote:

I am intrigued to see your code and see if I can spot anything. Is it possible to share your code. Also, can you please post the full error stack?

Thanks.

On 12 May 2012, at 18:04, John [email protected] wrote:

Unfortunately that completely misses the point. The point being that I don't have a User model. Instead I have a Member model. Even though CanCan allows you to override the default model of 'User' it seems that there's one place in the code where it's hard coded to assume you're using a User model. That shows up when using RSpec to test the views.

I think I'll have to dive into the code, fix it myself, and send you a pull request.


Reply to this email directly or view it on GitHub: https://github.com/ryanb/cancan/issues/598#issuecomment-5670063


Reply to this email directly or view it on GitHub: https://github.com/ryanb/cancan/issues/598#issuecomment-5672349

JohnSmall avatar May 13 '12 09:05 JohnSmall

Is it possible for you to create a dummy app with bare minimum feature that replicates this issue? I will pull it down and investigate this.

andhapp avatar May 13 '12 16:05 andhapp

OK, but this app is a side project so I can only work on it in my spare time. I'll send a copy or put it on Github when it's ready.

On 13 May 2012 17:23, Anuj Dutta < [email protected]

wrote:

Is it possible for you to create a dummy app with bare minimum feature that replicates this issue? I will pull it down and investigate this.


Reply to this email directly or view it on GitHub: https://github.com/ryanb/cancan/issues/598#issuecomment-5677356

JohnSmall avatar May 14 '12 04:05 JohnSmall

Okay. Thanks. I will create a test app and try and replicate the issue, if I get time.

andhapp avatar May 14 '12 12:05 andhapp

@ndgiang84: Hey, will you be able to create a dummy app that reproduces this issue and push it in a public repo somewhere and add the link to it in the comments.

@JohnSmall was meant to do it, but I guess he is quite busy.

Thanks.

andhapp avatar Jun 06 '12 08:06 andhapp

@andhapp thanks for your response I'll do so when I have time.

FYI, I'm using Authlogic 3.1.0 and Rails 3.1.3.

jobinthepast avatar Jun 06 '12 14:06 jobinthepast

If you stub the current_user method into the controller this will work fine

controller.stub(:current_user) { Account.new }

djcp avatar Oct 25 '12 17:10 djcp

Any news here?

luxflux avatar Feb 12 '13 12:02 luxflux

Having a similar issue (CanCan 1.6.9, Devise 2.1.2, Rails 3.2.11).

Is there any workarounds for this issue?

rhazegh avatar Feb 25 '13 00:02 rhazegh

I'm was having the same problem: the can? method being called in the view being tested was not aware of the current_user being stub in the test setup. I finally ended up stubbing the method both in the view and in the controller and now it works:

  before(:each) do
    ...
    user = User.new # or whatever
    view.stub!(:current_user).and_return(user)
    controller.stub!(:current_user).and_return(user)
  end

esti avatar Feb 25 '13 14:02 esti

I tried controller.stub!(:current_user).and_return(user) in the controller RSpec test but I am still getting CanCan::AccessDenied: You are not authorized to access this page.

When I access the controller with curl, it works perfectly and the authorization is enforced as intended.

EDIT: Problem resolved. See bellow...

rhazegh avatar Feb 25 '13 23:02 rhazegh

I debugged the code and it turns out the factory that created the user was not assigning any roles to the user. I fixed the factory and now it works perfectly. I didn't even need to stub the current_user.

rhazegh avatar Feb 26 '13 05:02 rhazegh

I'm also affected to this issue. My tests fails adding the following line in a partial named app/views/layouts/_navigation.html.haml, which is called directly from my layout app/views/layouts/application.html.haml:

- if can? :read, Job

I'm using STI to define roles in my models. Here is the factory for my admin:

FactoryGirl.define do
  factory :admin, parent: :user, class: :admin do
    type :admin
  end
end

Testing this view in my spec spec/views/layouts/_navigation.html.haml_spec.rb, I'm got the (minimized) following:

describe "my feature"
  before(:each) do
    admin = build(:admin)
    view.stub(:current_user).and_return(admin)
  end
  it "is a feature" do
    # Test
  end
end

With this code I've got

undefined local variable or method `current_user' for #<ActionView::TestCase::TestController:0x9fb15e0>

However, the test pass if I add this other stub right after the view stub:

controller.stub(:current_user).and_return(admin)

Full trace from the error:

   # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/cancan-1.6.10/lib/cancan/controller_additions.rb:357:in `current_ability'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/cancan-1.6.10/lib/cancan/controller_additions.rb:380:in `can?'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/abstract_controller/helpers.rb:53:in `can?'
     # ./app/views/layouts/_navigation.html.haml:5:in `_app_views_layouts__navigation_html_haml___562165619_96886850'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/template.rb:145:in `block in render'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.13/lib/active_support/notifications.rb:123:in `block in instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.13/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.13/lib/active_support/notifications.rb:123:in `instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/template.rb:143:in `render'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/template_renderer.rb:47:in `block (2 levels) in render_template'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/abstract_renderer.rb:38:in `block in instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.13/lib/active_support/notifications.rb:123:in `block in instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.13/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.13/lib/active_support/notifications.rb:123:in `instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/abstract_renderer.rb:38:in `instrument'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/template_renderer.rb:46:in `block in render_template'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/template_renderer.rb:54:in `render_with_layout'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/template_renderer.rb:45:in `render_template'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/template_renderer.rb:18:in `render'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/renderer.rb:36:in `render_template'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/renderer/renderer.rb:17:in `render'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/helpers/rendering_helper.rb:24:in `render'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/haml-4.0.3/lib/haml/helpers/action_view_mods.rb:12:in `render_with_haml'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/test_case.rb:170:in `render'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.13/lib/action_view/test_case.rb:115:in `render'
     # ./spec/views/layouts/_navigation.html.haml_spec.rb:18:in `block (3 levels) in <top (required)>'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example.rb:237:in `instance_eval'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example.rb:237:in `instance_eval'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/hooks.rb:21:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/hooks.rb:66:in `block in run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/hooks.rb:66:in `each'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/hooks.rb:66:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/hooks.rb:418:in `run_hook'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:334:in `run_before_each_hooks'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example.rb:300:in `run_before_each'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example.rb:113:in `block in run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example.rb:254:in `with_around_each_hooks'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example.rb:111:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:390:in `block in run_examples'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:386:in `map'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:386:in `run_examples'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:371:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:372:in `block in run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:372:in `map'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:372:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/command_line.rb:28:in `block (2 levels) in run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/command_line.rb:28:in `map'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/command_line.rb:28:in `block in run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/reporter.rb:34:in `report'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/command_line.rb:25:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/runner.rb:80:in `run'
     # /home/fotanus/.rvm/gems/ruby-2.0.0-p247/gems/rspec-core-2.13.1/lib/rspec/core/runner.rb:17:in `block in autorun'

fotanus avatar Aug 13 '13 23:08 fotanus

Thanks for your submission! The ryanb/cancan repository has been inactive since Sep 06, 2013. Since only Ryan himself has commit permissions, the CanCan project is on a standstill.

CanCan has many open issues, including missing support for Rails 4. To keep CanCan alive, an active fork exists at cancancommunity/cancancan. The new gem is cancancan. More info is available at #994.

If your pull request or issue is still applicable, it would be really appreciated if you resubmit it to CanCanCan.

We hope to see you on the other side!

xhoy avatar Jul 01 '14 07:07 xhoy