How to stub ApplicationController method in request spec
Solution 1
skalee seems to have provided the correct answer in the comment.
If the method you're trying to stub is an instance method (most likely) and not a class method then you need use:
ApplicationController.any_instance.stub(:current_user)
Solution 2
Here are a couple of examples of the basic form.
controller.stub(:action_name).and_raise([some error])
controller.stub(:action_name).and_return([some value])
In your particular case, I believe the proper form would be:
controller.stub(:current_user).and_return([your user object/id])
Here's a full working example from a project I work on:
describe PortalsController do
it "if an ActionController::InvalidAuthenticityToken is raised the user should be redirected to login" do
controller.stub(:index).and_raise(ActionController::InvalidAuthenticityToken)
get :index
flash[:notice].should eql("Your session has expired.")
response.should redirect_to(portals_path)
end
end
To explain my full example, basically what this does is verify that, when an ActionController::InvalidAuthenticityToken
error is raised anywhere in the app, that a flash message appears, and the user is redirected to the portals_controller#index
action. You can use these forms to stub out and return specific values, test an instance of a given error being raised, etc. There are several .stub(:action_name).and_[do_something_interesting]()
methods available to you.
Update (after you added your code): per my comment, change your code so it reads:
require 'spec_helper'
describe "Login" do
before(:each) do
@mock_controller = mock("ApplicationController")
@mock_controller.stub(:current_user).and_return(User.first)
end
it "logs in" do
visit '/'
page.should have_content("Hey there user!")
end
end
Solution 3
This works for me and gives me a @current_user
variable to use in tests.
I have a helper that looks like this:
def bypass_authentication
current_user = FactoryGirl.create(:user)
ApplicationController.send(:alias_method, :old_current_user, :current_user)
ApplicationController.send(:define_method, :current_user) do
current_user
end
@current_user = current_user
end
def restore_authentication
ApplicationController.send(:alias_method, :current_user, :old_current_user)
end
And then in my request specs, I call:
before(:each){bypass_authentication}
after(:each){restore_authentication}
Solution 4
For anyone else who happens to need to stub an application controller method that sets an ivar (and was stymied by endless wanking about why you shouldn't do that) here's a way that works, with the flavour of Rspec circa October 2013.
before(:each) do
campaign = Campaign.create!
ApplicationController.any_instance.stub(:load_campaign_singleton)
controller.instance_eval{@campaign = campaign}
@campaign = campaign
end
it stubs the method to do nothing, and sets the ivar on rspec's controller instance, and makes it available to the test as @campaign.
Solution 5
For Rspec 3+ the new api is:
For a controller test, nice and short:
allow(controller).to receive(:current_user).and_return(@user)
Or for all instances of ApplicationController:
allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(@user)
Matt Fordham
Updated on July 05, 2022Comments
-
Matt Fordham almost 2 years
I am needing to stub the response of a
current_user
method in an Rspec/capybara request spec. The method is defined inApplicationController
and is using helper_method. The method should simply return a user id. Within the test, I'd like this method to return the same user id each time.Alternatively, I could fix my problem by setting
session[:user_id]
in the spec (which is whatcurrent_user
returns)... but that doesn't seem to work either.Are either of these possible?
Edit:
Here is what I've got (it is not working. It just runs the normal current_user method).
require 'spec_helper' describe "Login" do before(:each) do ApplicationController.stub(:current_user).and_return(User.first) end it "logs in" do visit '/' page.should have_content("Hey there user!") end end
Also not working:
require 'spec_helper' describe "Login" do before(:each) do @mock_controller = mock("ApplicationController") @mock_controller.stub(:current_user).and_return(User.first) end it "logs in" do visit '/' page.should have_content("Hey there user!") end end
-
Matt Fordham over 12 yearsThanks for your response, but that looks like a controller spec, as opposed to a request spec. I am pretty new to testing, so I could be wrong :)
-
jefflunt over 12 yearsStub just acts as a replacer for any method defined on any class. In the example you gave, it is a method
current_user
defined onApplicationController
, correct? The same basic form should work, unless I'm misunderstanding your question. Can you post the best form of the test you've written, even if it's not working? -
jefflunt over 12 yearsSo, I was assuming that your test was inside a spec that starts with
describe ApplicationController
, so my bad there. If that's not the case, then you should still be able to doApplicationController.stub(:current_user).and_return([your user object/id])
-
jefflunt over 12 yearsOr maybe declare a mock object first for the ApplicationController, and then define the stub on that:
mock_controller = mock("ApplicationController")
followed bymock_controller.stub(:current_user).and_return([your user object/id])
- rspec.info/documentation/mocks -
Matt Fordham over 12 yearsI tried those both (see the code I added above). Still no luck. The original
current_user
method still gets called. -
jefflunt over 12 yearsThe code you posted declares
mock_controller
as a local variable (which is only accessible from within thebefore
method). Change the declaration to@mock_controller ...
-
Matt Fordham over 12 yearsDang. Still not working :( I edited the code above to reflect your suggestion. I really appreciate your help :)
-
Joe Sak over 12 yearsI am having the exact same problem in request specs and cannot figure it out!!
-
Joe Sak over 12 yearsAre you wrapping your def current_user code in if cookies[:auth_token] or something similar, by chance? Because that's what mine was doing so it still returned nil(false), so I stubbed User.find_by_auth_token! and returned my mock user, removed the if cookies[:auth_token] line, and now I'm good. It should still come back nil if there is no auth_token in the visitors' cookie
-
Joe Sak over 12 yearsBleh: you know what? That doesn't work when cookies[:auth_token] is not present since User.find_by_auth_token! raises an error for user not found... I wish we could just stub :current_user!! :P
-
bjnord about 12 yearsI'm trying to do something even more basic in a request spec:
WidgetController.stub(:index)
followed byget :index
. My actual WidgetController index method gets called every time; it's like the stub isn't there. I tried both forms listed in this answer. -
Arctodus almost 10 yearsThis still works for Rspec 3 but gives deprecation warning. This is the new syntax:
allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(your_test_user)
-
eggmatters about 9 yearsTool late to end the endless wanking but just in the nick of time to keep me from throwing my computer out the window. Thanks!
-
brntsllvn about 8 yearsLove this quote - "Using this feature is often a design smell" - from the Rspec docs.