2
votes

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!

3

3 Answers

2
votes

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
0
votes

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!

0
votes

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.