How to mock method call inside another method call - ruby-on-rails

I have the following code:
def do_something(param1, param2)
"hello" if current_page? param2
end
The application code above works however when I do this in my helper spec:
allow(helper).to receive(:current_page?).and_return(true)
it does not work it says You cannot use helpers that need to determine the current page unless your view context provides a Request object in a #request method
but when I do
allow(self).to receive(:current_page?).and_return(true)
it works. How is this happening? Isn't self in this case the helper spec class? Why can't I refer to the helper explicitly?

I guess you call the expect as below
RSpec.describe ApplicationHelper, type: :helper do
describe "#hello" do
it "returns hello" do
expect(do_something).to eq("hello")
end
end
end
Whenever you describe a test case, you create a RSpec::Rails::{type}ExampleGroup, here type is helper so you create a RSpec::Rails::HelperExampleGroup, this example_group wrap a helper that is your ApplicationHelper. Beside that, this example_group could respond_to all methods of your helper
RSpec.describe ApplicationHelper, type: :helper do
# ...
it "returns hello" do
puts self # RSpec::ExampleGroups::ApplicationHelper::...
puts self.respond_to?(:do_something) # true
puts self.respond_to?(:current_page?)# true
puts self.helper.is_a?(ApplicationHelper) # true
end
Here current_page? is special method which belongs to module ActionView::Helpers::UrlHelper and this is a guy raise the error you've got.
So when you call directly expect(do_something) that you're calling expect(example_group.do_something) and so that the object call the method current_page? inside the method do_something is the example_group and if you're not mock this method on the example_group object, this will raise error. That why your test case will pass when you mock allow(self).to receive(:current_page?).and_return(true).
Now your test case will pass if you mock this method on the helper method and also use the helper to call do_something since helper.do_something equivalent to self.helper.do_something that is actually example_group.helper.do_something
allow(helper).to receive(:current_page?).and_return(true)
expect(helper.do_something).to eq("hello")

Related

RSPEC NoMethodError: helper method calling another controller's action -

I have a controller file:
some_controller.rb:
class SomeController < ActionController::Base
def get_params
# Do something with params
end
end
And a helper file:
module SomeHelper
def modify_params
get_params
end
end
And rspec File for helper:
require 'spec_helper'
describe SomeHelper do
describe 'check_if_param_has_key' do
it "checks if param has key" do
modify_params.should eql(true)
end
end
Here, I am calling controller's method inside helper method. Now I am writing test cases for helper method modify_param.
But it throws error : NoMethodError: get_params
Is there a way to access controller's method inside helper spec?
Is this the right way to do it? Or Am I missing something in this?
Edited:
The controller is ApplicationController, and method inside it return string containing which controller/method was called on page load, by looking at params[:controller], params[:action]
As the author of RSpec has pointed out, helper tests are conceptually unrelated to controllers. So even if there was a way, you'd likely not want to bring the controller into the picture. You can easily stub out the method call in your spec:
describe SomeHelper do
describe "#foo" do
before do
allow(helper).to receive(:bar).and_return("hello")
end
it { expect(helper.foo).to eql("hello") }
end
end
Even if you had a controller, you'd likely have to stub the method call there, to cover the edge case.
Note that this stubbing a method not defined in the helper will fail if you're using verifying doubles.
My problem has been solved by making get request to the page:
describe 'check if the home page by checking params', :type => :request do
it 'return true if page is Home page' do
get "/homepage"
helper.modify_params.should eql(true)
end
end
In above code, after calling get request to homepage, helper method will have access to all the params and controller action it is calling. All my test cases have been passed.

Rails - How to mock or access methods defined in ApplicationController from helper spec?

I'm trying to write specs for a Rails helper. This helper calls a method
defined in ApplicationController and exposed through helper_method:
app/helpers/monkeys_helper.rb:
module MonkeysHelper
def current_monkey_banana_count
# current_monkey is defined in ApplicationController
current_monkey.present? ? current_monkey.banana_count : 0
end
end
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
helper_method :current_monkey
protected
def current_monkey
#current_monkey ||= Monkey.find(session[:monkey_id])
end
end
If I call current_monkey_banana_count from a view and access it through the browser, it works fine. But if I call it from a spec like this:
spec/helpers/monkeys_helper_spec.rb:
RSpec.describe MonkeysHelper, type: :helper do
describe "#current_monkey_banana_count" do
it "returns 0 if there is no monkey" do
expect(helper.current_monkey_banana_count).to eq 0
end
end
end
Then I get this error when I run the spec:
NameError:
undefined local variable or method `current_monkey' for #<#<Class:0x007fe1ed38d700>:0x007fe1e9c72d88>
Rspec documentation says:
To access the helper methods you're specifying, simply call them
directly on the helper object. NOTE: helper methods defined in
controllers are not included.
Any idea how to either mock current_monkey or make it visible from inside current_monkey_banana_count?
Thanks!
I found a (nasty) way to do it, but it works:
spec/helpers/monkeys_helper_spec.rb:
require 'rails_helper'
RSpec.describe CartsHelper, type: :helper do
before do
def helper.current_monkey; end
end
describe "#current_monkey_banana_count" do
it "returns 0 if there is no cart" do
expect(helper).to receive(:current_monkey).and_return(nil)
expect(helper.current_monkey_banana_count).to eq 0
end
it "returns monkey.banana_count if there is a monkey" do
expect(helper).to receive(:current_monkey).and_return(Monkey.create!(banana_count: 5))
expect(helper.current_monkey_banana_count).to eq 5
end
end
end
Maybe you can achieve that by mocking current_monkey in this way (have you tried it already?):
RSpec.describe MonkeysHelper, type: :helper do
let(:monkey) { create(:monkey) }
before do
allow(helper).to receive(:current_monkey_user) { monkey }
end
# your rest of code
end
Cheers!
View can call helper methods defined in controller because controller eval them automatically, please check code here.
But your helper test doesn't call controller, so that current_monkey isn't available on MonkeysHelper module. The best practice is helpers defined in controller call helper defined in helper class but not vice versa. In your case, you can move current_monkey to MonkeyHelper to be able to test it.

How to access a controller helper method from a helper test in Rails 4?

I've got a method defined in ApplicationController as a helper method.
helper_method :can_access_participant_contact_data?
I'm trying to write a test for a helper method that resides in a helper file. This helper method makes a call to helper_method :can_access_participant_contact_data?
# In participants_helper.rb
#
def redacted_contact_data participant, attribute_name
attribute = participant.try(:contact_data).try(attribute_name)
return attribute if can_access_participant_contact_data?(participant)
return nil if attribute.blank?
return attribute.gsub(/\S/i, '*') # Asterisked string
end
All I'm doing so far in my test is making a call to redacted_contact_data
require 'test_helper'
class ParticipantsHelperTest < ActionView::TestCase
test "should return an asterisked string with spaces" do
redacted_contact_data(Participant.first, :name)
end
end
When I run my test, I'm getting this message
undefined method `can_access_participant_contact_data?' for #<ParticipantsHelperTest:0x007fd6c7c6d608>
I've been having a look around but I'm not sure how to get around this issue. Do I need to mock can_access_participant_contact_data? somehow? or can I just include the method into the test?
AFAIK (As far as I know), you cannot fix this without stubbing, or doing some change in your code, as essentially a helper file is just a module of itself that should be treated independent of where it's gonna be included. Who knows you might want to include such helper file inside your model files for example, in which incidentally the model file also has a method named can_access_participant_contact_data? but does differently from that one defined in the ApplicationController, therefore you cannot unit test this without specifying the context / base.
Possible Workarounds:
Stubbing:
Use Mocha or rework testing into RSpec
Or manually (maybe there's a better way) by:
test "should return an asterisked string with spaces" do
ParticipantsHelper.class_eval do
define_method :can_access_participant_contact_data? do |arg|
true
end
end
redacted_contact_data(Participant.first, :name)
end
Or, moving all your ApplicationController helper methods into a separate/existing helper file, say inside your already existing ApplicationHelper. Then afterwards, include that helper inside your other helper file that you are testing that is making use of the method/s. i.e.:
# helpers/application_helper.rb
module ApplicationHelper
def can_access_participant_contact_data?(participant)
# YOUR CODE
end
end
# helpers/participants_helper.rb
module ParticipantHelper
include ApplicationHelper
def redacted_contact_data participant, attribute_name
attribute = participant.try(:contact_data).try(attribute_name)
return attribute if can_access_participant_contact_data?(participant)
return nil if attribute.blank?
return attribute.gsub(/\S/i, '*') # Asterisked string
end
end
If using this approach, then two ways to call the helper method inside the controller:
Use Rails helpers method inside a controller:
class ParticipantsController
def show
helpers.can_access_participant_contact_data?(#participant)
end
end
Or, include the helpers directly (I personally prefer the other approach just above)
class ApplicationController < ActionController::Base
include ApplicationHelper
end
class ParticipantsController < ApplicationController
def show
can_access_participant_contact_data?(#participant)
end
end
For the view files, you won't need to update any code.
Another idea is to do "helper test" in "controller test" as follows:
require 'test_helper'
class ParticipantsControllerTest < ActionController::TestCase
setup do
# do some initialization here. e.g. login, etc.
end
test "should return an asterisked string with spaces" do
participant = ...
get :show, id: participant.id
assert_equal '...', #controller.view_context.redacted_contact_data(...)
end
end
Where, #controller is ParticipantsController object already defined by rails controller testing framework (or you can explicitly define it when controller name is different from *ControllerTest), and view_context is the object for helper methods (see https://api.rubyonrails.org/classes/ActionView/Rendering.html#method-i-view_context for more detail).
Helper method often refer controller object and/or method (like session, request) so that it is sometimes difficult to do unit-test only in test/helpers/*. This is the reason why I test helper in controller in such a case.

how to test a common method in application_controller

I have an auth method and want to put it in my application_controller.
class ApplicationController < ActionController::Base
helper_method :check_cred
def check_cred
"within check cred"
end
but if I do this
require 'spec_helper'
describe ApplicationController do
it 'should check_cred', task050: true do
check_cred.should == 'within check cred'
end
end
I get:
undefined local variable or method `check_cred' for #<RSpec::Core::ExampleGroup::Nested_9:0x007ff5e3e40558>
How would I call a method like this to test?
thx
RSpec controller specs wrap ActionController::TestCase::Behavior, which provides some instance variables to be used during tests:
Special instance variables
ActionController::TestCase will also automatically provide the following instance
variables for use in the tests:
#controller:
The controller instance that will be tested.
So you may be able to do the following:
it 'should check_cred', task050: true do
#controller.check_cred.should == 'within check cred'
end
Alternatively, you could move this helper method out into a separate helper module, and use an RSpec helper spec to perform the test, which may prove to be a better way to structure this test.

How to call an app helper method from an RSpec test in Rails?

The title is self explanatory.
Everything I've tried led to a "undefined method".
To clarify, I am not trying to test a helper method. I am trying to use a helper method in an integration test.
You just need to include the relevant helper module in your test to make the methods available:
describe "foo" do
include ActionView::Helpers
it "does something with a helper method" do
# use any helper methods here
It's really as simple as that.
For anyone coming late to this question, it is answered on the Relish site.
require "spec_helper"
describe "items/search.html.haml" do
before do
controller.singleton_class.class_eval do
protected
def current_user
FactoryGirl.build_stubbed(:merchant)
end
helper_method :current_user
end
end
it "renders the not found message when #items is empty" do
render
expect(
rendered
).to match("Sorry, we can't find any items matching "".")
end
end
If you are trying to use a helper method on your view test, you can go with the following:
before do
view.extend MyHelper
end
It must be inside a describe block.
It works for me on rails 3.2 and rspec 2.13
Based on Thomas Riboulet's post on Coderwall:
At the beginning of your spec file add this:
def helper
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::NumberHelper
end
and then call a particular helper with helper.name_of_the_helper.
This particular example includes the ActionView's NumberHelper. I needed the UrlHelper, so I did include ActionView::Helpers::UrlHelper and helper.link_to.
As you can see here https://github.com/rspec/rspec-rails , you should initialize the spec/ directory (where specs will reside) with:
$ rails generate rspec:install
this will generate an rails_helper.rb with the option
config.infer_spec_type_from_file_location!
and finally require the new rails_helper in you helper_spec.rb instead of requiring 'spec_helper'.
require 'rails_helper'
describe ApplicationHelper do
...
end
good luck.
I'm assuming you're trying to test the helper method. In order to do this you'll have to put your spec file into spec/helpers/. Given you're using the rspec-rails gem, this will provide you a helper method that allows you to call any helper method on it.
There's a nice example over in the official rspec-rails documentation:
require "spec_helper"
describe ApplicationHelper do
describe "#page_title" do
it "returns the default title" do
expect(helper.page_title).to eq("RSpec is your friend")
end
end
end

Resources