Home » Ruby » How to stub ApplicationController method in request spec

How to stub ApplicationController method in request spec

Posted by: admin November 30, 2017 Leave a comment

Questions:

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
Answers:

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)

Questions:
Answers:

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

Questions:
Answers:

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}

Questions:
Answers:

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.

Questions:
Answers:

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)