How to stub a helper method?

Hi there

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?

what am I missing here?

why not allow(ActionView::Base).to receive(:is_happy?).and_return(false)?

I already tried this option, I also tried allow_any_instance_of, unfortunately, it keep giving me the same error:

Failure/Error: allow(ActionView::Base).to receive(:is_happy?).and_return(false)
       ActionView::Base does not implement: is_happy?

Using allow_any_instance_of:

Failure/Error: allow_any_instance_of(ActionView::Base).to receive(:is_happy?).and_return(false)
       ActionView::Base does not implement #is_happy?

Ooops I misunderstood the error, maybe:

allow_any_instance_of(ApplicationController).to receive(:is_happy?).and_return(false)

Same problem. I keep getting:

NoMethodError:
       undefined method `is_happy?' for #<ActionView::Base:0x0000000000a960>

With a help, the only way I could make it work, was to create this method:

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

and on before block, do this:

before do
   inject_controller_helper_methods(ApplicationController)
end

It works, but feels like there could be a better solution.

Demo code: GitHub - weezhard0/demo-rails-rspec

You can just define the method in your spec file. The RSpec DSL is creating classes behind the scenes and your helper module is just mixed into those classes. Since you want both possible answers you can use the context to create different classes with different answers. So:

describe ApplicationHelper, type: :helper do
  describe 'mood text' do
    context 'happy' do
      it 'returns "Happy" text' do
        expect( show_mood_text ).to eq 'Happy'
      end

      def is_happy? = true
    end

    context 'sad' do
      it 'returns "Sad" text' do
        expect( show_mood_text ).to eq 'Sad'
      end

      def is_happy? = false
    end
  end
end

I haven’t run the above but I believe it should work. No rspec mocking needed.

Some side style issues. Just advice so ignore if you like it your way.

  • Just name the method happy?. In Ruby the is_ prefix is generally not used as the ? suffix implies a boolean response.
  • show_mood_text might be better named just mood_text
1 Like

Hi @e_a

I am using this solution:

Thank you for the advice