How to stub ApplicationController method in request spec

Ruby on-RailsRubyRspecCapybara

Ruby on-Rails Problem Overview


I am needing to stub the response of a current_user method in an Rspec/capybara request spec. The method is defined in ApplicationController 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 what current_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

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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 - Ruby on-Rails

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 - Ruby on-Rails

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 - Ruby on-Rails

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 - Ruby on-Rails

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)

Solution 6 - Ruby on-Rails

None of the provided responses worked for me. As in @matt-fordam's original post, I have a request spec, not a controller spec. The test just renders the view without launching a controller.

I resolved this by stubbing the method on the view as described in this other SO post

view.stub(:current_user).and_return(etc)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatt FordhamView Question on Stackoverflow
Solution 1 - Ruby on-Railsfess .View Answer on Stackoverflow
Solution 2 - Ruby on-RailsjeffluntView Answer on Stackoverflow
Solution 3 - Ruby on-RailsiHiDView Answer on Stackoverflow
Solution 4 - Ruby on-RailsMichael JohnstonView Answer on Stackoverflow
Solution 5 - Ruby on-RailsSbbsView Answer on Stackoverflow
Solution 6 - Ruby on-RailsjwadsackView Answer on Stackoverflow