Вспомогательный метод в контроллере (созданный с помощью `helper_method`) недоступен во вспомогательном тесте RSpec

Допустим, у меня есть следующий код на ApplicationController:

class ApplicationController < ActionController::Base
   helper_method :is_happy?

   private

   def is_happy?
      true
   end
end

а потом, на моем ApplicationHelper у меня есть метод, который я хотел бы протестировать следующим образом:

def show_mood_text
  return 'Happy' if is_happy?

  'Sad'
end

Я хотел бы проверить оба случая, когда is_happy? возвращает true и когда возвращает false. Итак, мой вариант состоял в том, чтобы заглушить метод is_happy?. Для этого я сделал следующее:

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

Но когда я пытаюсь запустить этот тест, я получаю следующую ошибку:

Failure/Error: allow(helper).to receive(:is_happy?).and_return(false)
       #<ActionView::Base:0x00000000009e70> does not implement: is_happy?

Обновлено:

Код демо: https://github.com/weezhard0/demo-rails-rspec

$ bundle install
$ bin/rspec

что мне здесь не хватает?

🤔 А знаете ли вы, что...
Ruby on Rails предоставляет множество готовых решений для обработки типичных задач, таких как маршрутизация и работа с базой данных.


96
2

Ответы:

Решено

Это, по-видимому, известная «проблема», согласно, например, этой странице Relish:

ПРИМЕЧАНИЕ: вспомогательные методы, определенные в контроллерах, не включены.

С учетом сказанного, есть некоторые уловки, которые можно сделать, чтобы сделать вспомогательные методы, определенные с помощью helper_method, доступными внутри вашего вспомогательного файла спецификации.

После некоторого исследовательского исследования я смог написать (и успешно протестировать) файл теста/спецификации (spec/helpers/application_helper_spec.rb) ниже, используя Rails 6.1.7, RSpec 3.11.0, rspec-rails 6.0.1 и Ruby 3.0.3, и он предоставляет то, что вы ищете:

# 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

Попросив помощи в репозитории github RSpec-rails, я получил следующее решение:

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

Оно работает.

Ссылка: https://github.com/rspec/rspec-rails/issues/2654#issuecomment-1404983567