Unable to stub helper method with rspec
Asked Answered
B

6

29

I am trying to stub a method on a helper that is defined in my controller. For example:

class ApplicationController < ActionController::Base
  def current_user
    @current_user ||= authenticated_user_method
  end
  helper_method :current_user
end

module SomeHelper
  def do_something
    current_user.call_a_method
  end
end

In my Rspec:

describe SomeHelper
  it "why cant i stub a helper method?!" do
    helper.stub!(:current_user).and_return(@user)
    helper.respond_to?(:current_user).should be_true # Fails
    helper.do_something # Fails 'no method current_user'
  end
end

In spec/support/authentication.rb

module RspecAuthentication
  def sign_in(user)
    controller.stub!(:current_user).and_return(user)
    controller.stub!(:authenticate!).and_return(true)

    helper.stub(:current_user).and_return(user) if respond_to?(:helper)
  end
end

RSpec.configure do |config|
  config.include RspecAuthentication, :type => :controller
  config.include RspecAuthentication, :type => :view
  config.include RspecAuthentication, :type => :helper
end

I asked a similar question here, but settled on a work around. This strange behavior has creeped up again and I would like to understand why this doesnt work.

UPDATE: I have found that calling controller.stub!(:current_user).and_return(@user) before helper.stub!(...) is what is causing this behavior. This is easy enough to fix in spec/support/authentication.rb, but is this a bug in Rspec? I dont see why it would be expected to not be able to stub a method on a helper if it was already stubbed on a controller.

Bullivant answered 10/5, 2012 at 16:1 Comment(5)
Try stubbing the method through ApplicationController, since that is where it is defined. ApplicationController.stub(:current_user => @user) EDIT: Now I'm thinking that might not work.Pipistrelle
No, it didnt work. I tried any_instance too with no luck. I have actually got it working, but I am a bit confused (may have found a bug with rspec). I will update the question shortly.Bullivant
Sounds like a bug. It's definitely worth filing an issue at github.com/rspec/rspec-mocks/issues (and trying with the latest version).Migdaliamigeon
Created ticket: github.com/rspec/rspec-mocks/issues/135Bullivant
i have same issue, does have any solutions yet?Fulmis
M
8

Try this, it worked for me:

describe SomeHelper
  before :each do
    @helper = Object.new.extend SomeHelper
  end

  it "why cant i stub a helper method?!" do
    @helper.stub!(:current_user).and_return(@user)
    # ...
  end
end

The first part is based on this reply by the author of RSpec, and the second part is based on this Stack Overflow answer.

Maladminister answered 13/7, 2012 at 4:41 Comment(1)
... and then the tests actually need to test @helper, not SomeHelper.Hydrastinine
E
22

Update to Matthew Ratzloff's answer: You don't need the instance object and stub! has been deprecated

it "why can't I stub a helper method?!" do
  helper.stub(:current_user) { user }
  expect(helper.do_something).to eq 'something'
end

Edit. The RSpec 3 way to stub! would be:

allow(helper).to receive(:current_user) { user }

See: https://relishapp.com/rspec/rspec-mocks/v/3-2/docs/

Eximious answered 20/11, 2013 at 5:17 Comment(1)
I fixed my answer for RSpec 3.Eximious
C
19

In RSpec 3.5 RSpec, it seems like helper is no longer accessible from an it block. (It will give you the following message:

helper is not available from within an example (e.g. an it block) or from constructs that run in the scope of an example (e.g. before, let, etc). It is only available on an example group (e.g. a describe or context block).

(I can't seem to find any documentation on this change, this is all knowledge gained experimentally).

The key to solving this is knowing that helper methods are instance methods, and that for your own helper methods it's easy to do this:

allow_any_instance_of( SomeHelper ).to receive(:current_user).and_return(user) 

This is what finally worked for me

Footnotes/Credit Where Credit Due:

Charmine answered 18/6, 2017 at 14:35 Comment(0)
M
8

Try this, it worked for me:

describe SomeHelper
  before :each do
    @helper = Object.new.extend SomeHelper
  end

  it "why cant i stub a helper method?!" do
    @helper.stub!(:current_user).and_return(@user)
    # ...
  end
end

The first part is based on this reply by the author of RSpec, and the second part is based on this Stack Overflow answer.

Maladminister answered 13/7, 2012 at 4:41 Comment(1)
... and then the tests actually need to test @helper, not SomeHelper.Hydrastinine
P
5

Rspec 3

  user = double(image: urlurl)
  allow(helper).to receive(:current_user).and_return(user)
  expect(helper.get_user_header).to eq("/uploads/user/1/logo.png")
Piston answered 22/9, 2016 at 5:19 Comment(0)
F
5

As of RSpec 3.10, this technique will work:

  before do
    without_partial_double_verification { 
      allow(view).to receive(:current_user).and_return(user)
    }
  end

The without_partial_double_verification wrapper is needed to avoid a MockExpectationError unless you have that turned off globally.

Fleabite answered 23/7, 2021 at 17:22 Comment(1)
I was in a controller spec so I used allow(controller).to receive. The without_partial_double_verification was not needed in my case. Worked like a charmOffish
Z
3

This worked for me in the case of RSpec 3:

let(:user) { create :user }
helper do
  def current_user; end
end
before do
  allow(helper).to receive(:current_user).and_return user
end
Zomba answered 6/10, 2017 at 12:46 Comment(1)
Very clever. First you define missing method (from another helper, controller) in your helper and then stub it. Can the def ... block use directly user, so no stub is needed?Moving

© 2022 - 2024 — McMap. All rights reserved.