Lets say I have the following code on ApplicationController
:
class ApplicationController < ActionController::Base
helper_method :is_happy?
private
def is_happy?
true
end
end
and then, on my ApplicationHelper
I have a method, which I would like to test like this:
def show_mood_text
return 'Happy' if is_happy?
'Sad'
end
I would like to test both cases, when is_happy?
returns true
and when it returns false
. So, my option was to stub is_happy?
method. For that, I did the following:
it 'returns Sad when when is sad' do
allow(helper).to receive(:is_happy?).and_return(false)
expect(helper.show_mood_text).to eq "Sad"
end
But when I try to run this test, I got the following error:
Failure/Error: allow(helper).to receive(:is_happy?).and_return(false)
#<ActionView::Base:0x00000000009e70> does not implement: is_happy?
EDIT:
Demo code: https://github.com/weezhard0/demo-rails-rspec
$ bundle install
$ bin/rspec
what am I missing here?
CodePudding user response:
This is apparently a known "issue", according, for example, to this Relish page:
NOTE: helper methods defined in controllers are not included.
Having that said, there are some gimmicks that can be done in order to make helper methods defined through helper_method
reachable inside your helper spec file.
After some exploratory investigation, I could write (and successfully test) the test/spec file (spec/helpers/application_helper_spec.rb
) below using Rails 6.1.7, RSpec 3.11.0, rspec-rails 6.0.1 and Ruby 3.0.3, and it delivers what you're looking for:
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ApplicationHelper do
describe '#show_mood_text' do
subject { helper.show_mood_text }
before { inject_controller_helper_methods(ApplicationController) }
context 'when is_happy? returns true' do
before { allow(helper).to receive(:is_happy?).and_return(true) }
it { is_expected.to eq('Happy') }
end
context 'when is_happy? returns false' do
before { allow(helper).to receive(:is_happy?).and_return(false) }
it { is_expected.to eq('Sad') }
end
end
def inject_controller_helper_methods(controller_class)
helper_module = (controller = controller_class.new)._helpers
helper_methods = helper_module.instance_methods(false).sort
helper_method = ->(method) { helper_module.instance_method(method) }
helper_methods.each do |method|
helper.class.define_method(method, helper_method[method])
end
helper.controller = controller
end
end
CodePudding user response:
Asking for help on RSpec-rails github repository, I was given the following solution:
require 'rails_helper'
RSpec.describe ApplicationHelper do
describe '#show_mood' do
around(:example) do |example|
without_partial_double_verification do
example.call
end
end
context 'when is happy' do
before do
allow(helper).to receive(:happy?).and_return(true)
end
it { expect(helper.show_mood).to eq('Happy') }
end
context 'when is not happy' do
before do
allow(helper).to receive(:happy?).and_return(false)
end
it { expect(helper.show_mood).to eq('Sad') }
end
end
end
It works.
Ref.: https://github.com/rspec/rspec-rails/issues/2654#issuecomment-1404983567