代码之家  ›  专栏  ›  技术社区  ›  user2490003

在视图上下文中模拟所有Rails路由

  •  0
  • user2490003  · 技术社区  · 6 年前

    rails控制器和视图提供了 view_context (通常是 ActionView::Base 对象)提供用于生成视图的上下文。

    一个常见的模式是在Presenter类中包装模型实例,在这种情况下 视图上下文 通常也作为参数传递,以便演示者可以调用视图方法(例如 I8n.t() ,Rails路径助手等)。

    在我的rspec测试中,我使用mock来测试 视图上下文 演示者内部的行为。对于路径助手,我必须分别模拟每个路径:

    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
    

    是否有一种简单的方法可以同时以编程方式模拟所有路径?

    我想我可以在路径列表中循环(不知道该怎么做)并逐个模拟每个路径,但感觉不是正确的方法。

    谢谢!

    编辑: 实际上,上面的片段甚至都不正确。它抛出一个错误,因为 视图上下文 ( actionview::基本 )甚至没有实现 :some_custom_path 首先。我猜这是一个保护措施,以防刺伤不存在的东西。

    0 回复  |  直到 6 年前
        1
  •  1
  •   Fito von Zastrow    6 年前

    你为什么要嘲笑所有的路?

    我想你对 事实上 嘲笑这些电话,而不仅仅是存根。看到区别 here .

    不同的演示者可能会调用不同的 path 他们的方法 view_context . 我 推荐 您只显式模拟要在测试的演示者中调用的路径。

    你不需要嘲笑所有的路径,因为它们并不是每次都会被调用。

    我会把你的测试写如下:

    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 将保护您不受未在接收器上实现的方法的干扰。

    我愿意 不推荐 您可以这样做,但是如果您所要寻找的只是一个视图对象,它将无声地接受对 路径 方法然后您可以创建一个伪视图,如下所示:

    class FakeView
      private
    
      def view_methods
        ActionView::Base.instance_methods - Object.instance_methods
      end
    
      def method_missing(meth, *params, &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