Имитация всех маршрутов Rails в view_context

#ruby-on-rails #rspec #rails-routing #rspec3 #presenter

#ruby-on-rails #rspec #rails-маршрутизация #rspec3 #ведущий

Вопрос:

Контроллер Rails и представления предоставляют view_context (обычно ActionView::Base объект), который предоставляет контекст для генерации представлений.

Распространенным шаблоном является перенос экземпляров модели в класс Presenter, и в этом случае view_context обычно также передается в качестве аргумента, чтобы Presenter мог вызывать методы просмотра (например I8n.t() , помощники Rails path и т.д.) по мере необходимости.

В моих тестах RSpec я использую макет для проверки view_context поведения в Presenter. В частности, для помощников пути я должен имитировать каждый путь по отдельности:

 view_context = ActionView::Base.new
user = UserPresenter.new(FactoryBot.create(:user), view: view_context)

allow(view_context).to receive(:some_custom_path) do |opts|
  some_custom_path(opts)
end
  

Есть ли простой способ программно имитировать все пути сразу?

Я полагаю, я мог бы перебрать список путей (не уверен, как это сделать) и имитировать каждый по одному, но мне кажется, что это неправильный подход.

Спасибо!

РЕДАКТИРОВАТЬ: На самом деле приведенный выше фрагмент даже неверен. Он выдает ошибку, потому что view_context ( ActionView::Base ) даже не реализуется :some_custom_path в первую очередь. Я предполагаю, что это мера защиты от заглушения чего-то, чего не существует.

Ответ №1:

Почему вы хотите имитировать все пути?

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

Разные докладчики, вероятно, будут вызывать разные path методы на своих view_context . Я рекомендую вам явно имитировать только те пути, которые вы ожидаете вызвать в тестируемом вами presenter.

Вам не нужно имитировать все пути, потому что не все они будут вызываться каждый раз.

Я бы написал ваш тест следующим образом:

 describe UserPresenter do
  subject(:user_presenter) { described_class.new(user, view: view_context)

  let(:user) { FactoryBot.create(:user) }
  let(:view_context) { instance_double(ActionView::Base) }
  let(:some_custom_path) { 'some/custom/path' }

  before do
    allow(view_context).to receive(:some_custom_path).and_return(some_custom_path)
  end

  it 'does something'
end
  

Что касается ошибки, которую вы видите, да, instance_double защитит вас от заглушения метода, который не реализован в получателе.

Я не рекомендую вам это делать, но если все, что вы ищете, это объект view, который будет молча проглатывать вызовы path методов, тогда вы можете создать поддельное представление, подобное этому:

 class FakeView
  private

  def view_methods
    ActionView::Base.instance_methods - Object.instance_methods
  end

  def method_missing(meth, *params, amp;block)
    view_methods.include?(meth) ? nil : super
  end
end
  

а затем используйте его в своих тестах, таких как:

 describe UserPresenter do
  subject(:user_presenter) { described_class.new(user, view: view_context)

  let(:user) { FactoryBot.create(:user) }
  let(:view_context) { FakeView.new }

  it 'does something'
end