I am trying to stub a helper method from my helper:
# sessions_helper.rb
require 'rest_client'
module SessionsHelper
BASE_URL = "http://localhost:1234"
def current_user?(token)
sessions_url = BASE_URL + "/sessions"
headers = {"X-AuthToken" => 12345}
begin
RestClient.get(sessions_url, headers)
return true
rescue RestClient::BadRequest
return false
end
end
end
I try to stub the current_user? to always return true in my unit test:
require 'spec_helper'
describe SessionsHelper do
it "Should not get current user with random token" do
SessionsHelper.stub(:current_user?).and_return(true)
expect(current_user?(12345)).to eq(false)
end
end
But the test still pass (I expect it returns true instead).
Is there something I miss to configure the stub method?
Thanks
You are not calling the method on SessionsHelper. You are calling it on self. So try stubbing the method on self.
describe SessionsHelper do
it "Should not get current user with random token" do
stub(:current_user?).and_return(true)
expect(current_user?(12345)).to eq(false)
end
end
any_instance should do what you want:
SessionsHelper.any_instance.stub(:current_user?).and_return(true)
As #Vimsha says, you got confused between the module and the instance, which is easy to do.
Related
I've been trying to stub a private module method for the whole day now but with not progress.
Here is a snippet of my application controller class
class ApplicationController < ActionController::Base
include Cesid::Application
end
Cesid > Application.rb
module Cesid
module Application
extend ActiveSupport::Concern
included do
before_action :track_marketing_suite_cesid, only: [:new]
end
private
def track_marketing_suite_cesid
return unless id_token_available?
## #cesid_auth = Auth.new(#id_token)
#cesid_auth = Auth.new(id_token)
return unless #cesid_auth.present? && #cesid_auth.valid?
#cesid_admin = Admin.where(email: #cesid_auth.email).first_or_initialize
end
def id_token_available?
## #id_token.present?
id_token.present?
end
def id_token
#id_token ||= id_token_param
end
def id_token_param
cookies[:id_token]
end
end
end
Now, I'm trying to create a simple unit test for the method
id_token_available?
And I am just trying to set the id_token_param to a random value.
I've tried using this code as stated Is there a way to stub a method of an included module with Rspec?
allow_any_instance_of(Cesid).to receive(:id_token_param).and_return('hello')
but I just get this error
NoMethodError:
undefined method `allow_any_instance_of' for #<RSpec::ExampleGroups::CesidApplication::CesidAuthorizations::GetCesidApplication:0x00007fa3d200c1c0> Did you mean? allow_mass_assignment_of
Rspec file
require 'rails_helper'
describe Cesid::Application, :type => :controller do
describe 'cesid application' do
before do
allow_any_instance_of(ApplicationController).to receive(:id_token_param).and_return('hello')
end
it 'returns true if the id_token is present' do
expect(Cesid::Application.send('id_token_available?')).to eql(true)
end
end
end
Rspec version
3.5.4
This is honestly starting to drive me crazy
I see three issues:
You call allow_any_instance_of in a context in which it is not defined. allow_any_instance_of can be used in before blocks. I need to see your RSpec code to be more specific.
Actually your code is called on the ApplicationController, not on the module, therefore you need to change your stub to
allow_any_instance_of(ApplicationController).to receive(:id_token_param).and_return('hello')
Currently id_token_param will not be called at all, because id_token_available? checks the instance variable and not the return value of the id_token method that calls the id_token_param. Just change the id_token_available? to:
def id_token_available?
id_token.present?
end
There's a much better way of going about this test. The type: :controller metadata on your spec gives you an anonymous controller instance to work with.
Here's an example of how you could write this to actually test that the before_action from your module is used:
describe Cesid::Application, type: :controller do
controller(ApplicationController) do
def new
render plain: 'Hello'
end
end
describe 'cesid before_action' do
before(:each) do
routes.draw { get 'new' => 'anonymous#new' }
cookies[:id_token] = id_token
allow(Auth).to receive(:new).with(id_token)
.and_return(instance_double(Auth, valid?: false))
get :new
end
context 'when id token is available' do
let(:id_token) { 'hello' }
it 'sets #cesid_auth' do
expect(assigns(:cesid_auth)).to be_present
end
end
context 'when id token is unavailable' do
let(:id_token) { '' }
it 'does not set #cesid_auth' do
expect(assigns(:cesid_auth)).to be_nil
end
end
end
end
I have a method in my ApplicationHelper that checks to see if there are any items in my basket
module ApplicationHelper
def has_basket_items?
basket = Basket.find(session[:basket_id])
basket ? !basket.basket_items.empty? : false
end
end
Here is my helper spec that I have to test this:
require 'spec_helper'
describe ApplicationHelper do
describe 'has_basket_items?' do
describe 'with no basket' do
it "should return false" do
helper.has_basket_items?.should be_false
end
end
end
end
however when I run the test i get
SystemStackError: stack level too deep
/home/user/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_dispatch/testing/test_process.rb:13:
From debugging this i see that session is being accessed in ActionDispatch::TestProcess from #request.session, and #request is nil. When i access the session from my request specs #request is an instance of ActionController::TestRequest.
My question is can I access the session object from a helper spec? If I can, how? And if I cant what is the best practice to test this method?
****UPDATE****
This was down to having include ActionDispatch::TestProcess in my factories. Removing this include sorts the problem.
can I access the session object from a helper spec?
Yes.
module ApplicationHelper
def has_basket_items?
raise session.inspect
basket = Basket.find(session[:basket_id])
basket ? !basket.basket_items.empty? : false
end
end
$ rspec spec/helpers/application_helper.rb
Failure/Error: helper.has_basket_items?.should be_false
RuntimeError:
{}
The session object is there and returns an empty hash.
Try reviewing the backtrace in more detail to find the error. stack level too deep usually indicates recursion gone awry.
You are testing has_basket_items? action in ApplicationHelper, which check a specfic basket with a basket_id in the baskets table, so you should have some basket objects in your test which you can create using Factory_Girl gem.
Hers's an example :-
basket1 = Factory(:basket, :name => 'basket_1')
basket2 = Factory(:basket, :name => 'basket_2')
You can get more details on How to use factory_girl from this screen cast http://railscasts.com/episodes/158-factories-not-fixtures
It will create a Factory object in your test database. So, basically you can create some factory objects and then set a basket_id in session to check for its existence like this :
session[:basket_id] = basket1.id
So, your test should be like this :-
require 'spec_helper'
describe ApplicationHelper do
describe 'has_basket_items?' do
describe 'with no basket' do
it "should return false" do
basket1 = Factory(:basket, :name => 'basket_1')
basket2 = Factory(:basket, :name => 'basket_2')
session[:basket_id] = 1234 # a random basket_id
helper.has_basket_items?.should be_false
end
end
end
end
Alternatively, you can check for a basket_id which is being created by factory_girl to be_true by using :
session[:basket_id] = basket1.id
helper.has_basket_items?.should be_true
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.
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/
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:
Super Props to a blog entry by Johnny Ji about their struggles stubbing helper/instance methods
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.
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")
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
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.
In my helper module, I have:
def abc(url)
...
if request.env['HTTP_USER_AGENT']
do something
end
end
In my spec file, I have:
describe "#abc" do
before(:each) do
#meth = :abc
helper.request.env['HTTP_USER_AGENT'] = "..."
end
it "should return the webstart jnlp file" do
#obj.send(#meth, "some_url").should ....
end
end
When I run the spec I have this error:
undefined local variable or method `request' for <ObjectWithDocHelperMixedIn:0x00000103b5a7d0>
How do I stub for request.env['...'] in my specs?
Thanks.
If you're using rspec-rails, you might be able to use controller.request in your helper tests.
Well, you've almost nothing to do:
before(:each) do
#meth = :abc
request.env['HTTP_USER_AGENT'] = "..."
end
I just gave this another try and this passes:
#in helper
def foo
request.env['HTTP_USER_AGENT']
end
#spec
it "foo" do
helper.request.env['HTTP_USER_AGENT'] = 'foo'
expect(helper.foo).to eq 'foo'
end
You can override user-agent set in the request env by doing the following.
before(:each) do
#meth = :abc
helper.request.user_agent = 'something else'
end
Then, in your spec:
it "does stuff" do
expect(helper.send(#meth, "some_url")).to # ...
end
for those who use request specs instead of controller specs and want to set request.env can do it like this:
Rails.application.env_config["whatever"] = "whatever"
this will make request.env["whatever"] available in your controllers with value you gave it in your specs.
Try this:
stub(request).env { {"HTTP_USER_AGENT" => "Some String"} }
I would like write RSpec for my controller using RR.
I wrote following code:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe RegistrationController do
it "should work" do
#deploy and approve are member functions
stub.instance_of(Registration).approve { true }
stub.instance_of(Registration).deploy { true }
post :register
end
end
However RR stubs only deploy method when still calls original approve method.
What syntax should I use to stub both method calls for all instances of Registration class?
UPDATE:
I achivied desired result with [Mocha]
Registration.any_instance.stubs(:deploy).returns(true)
Registration.any_instance.stubs(:approve).returns(true)
It would appear the behavior you describe is actually a bug:
http://github.com/btakita/rr/issues#issue/17
as far as I know, the RSpec mocks don't allow you to do that. Are you sure, that you need to stub all instances? I usually follow this pattern:
describe RegistrationController do
before(:each) do
#registration = mock_model(Registration, :approve => true, :deploy => true)
Registration.stub!(:find => #registration)
# now each call to Registration.find will return my mocked object
end
it "should work" do
post :register
reponse.should be_success
end
it "should call approve" do
#registration.should_receive(:approve).once.and_return(true)
post :register
end
# etc
end
By stubbing the find method of the Registration class you control, what object gets returned in the spec.