#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