如何从控制器外部访问Rails控制器视图上下文?

11

我正在清理一些依赖于自定义控制器帮助方法的代码,通过创建一个“普通的 Ruby”Presenter对象来实现。在我的控制器中,我能够将视图上下文传递给该类:

def show
  # old code: view_context.bad_helper_method
  @foobar = FoobarPresenter.new(Foobar.find(params[:id]), view_context)
end

class FoobarPresenter
  def initialize(model, view_context)
    @model = model
    @view_context = view_context
  end

  def something
    @view_context.bad_helper_method
  end
end

然而,我不确定在我的测试中传递什么。我宁愿动态地提取helper/view_context,以便无需传递它。

我如何在控制器之外访问视图/控制器助手上下文?

这是一个Rails 3.2项目。

5个回答

14
比你想象的要简单!(我花了将近一个小时才找到方法)
你可以实例化一个ActionView。
_view_context = ActionView::Base.new

并在您的测试中使用它

FoobarPresenter.new(Foobar.new, _view_context)

1
谢谢!我试图在Rails控制台中实例化一个,这个方法帮了我。 - Philihp Busby
5
在现代的 Rails(例如 6.1 版本),执行 ActionView::Base.new 时会报错,错误信息为 wrong number of arguments (given 0, expected 3) - jrochkind
在Rails 7中,您可以使用控制器来创建一个视图上下文,例如:_view_context = InvoicesController.new.view_context - Steve Meisner

6
如何测试期望值?
  1. Test for controller (note that subject is the instance of the controller, assuming we're testing using rspec-rails):

    view_context     = double("View context")
    foobar_presenter = double("FoobarPresenter")
    
    allow(subject).to receive(:view_context).and_return(view_context)
    allow(FoobarPresenter).to receive(:new).with(1, view_context).and_return(foobar_presenter)
    
    get :show, id: 1
    
    expect(assigns(:foobar)).to eql(foobar_presenter)
    
  2. Test for presenter:

    view_context = double('View context', bad_helper_method: 'some_expected_result')
    presenter    = FoobarPresenter.new(double('Model'), view_context)
    
    expect(presenter.something).to eql('some_expected_result')
    

我认为这可能是最好的方法。谢谢! - Andrew

1
我很抱歉无法给你一个完美的答案。不过,我已经查看了Draper Decorator库,并且他们解决了这个问题。
特别地,他们有一个HelperProxy类和一个ViewContext类,似乎可以自动推断出你想要的上下文。

https://github.com/drapergem/draper

他们还有一些关于这两个类的规格,我相信你可以从中借鉴,以设置自己的规格。


这是个好主意。我会深入源代码,看看能否找到什么东西。 - Andrew

0

不幸的是,它只能在控制器内部使用,但是创建它相当容易。

class ViewContextDelegator
  RouteHelpers = Module.new { delegate_missing_to 'Rails.application.routes.url_helpers' }
  HELPERS = ApplicationController.helpers.extend(RouteHelpers)
  delegate_missing_to(:HELPERS)
end

然后在任何文件中使用它,如下所示:

ViewContextDelegator.new.time_ago_in_words(Time.now)
# "less than a minute"
ViewContextDelegator.new.root_path
# "/"

或者在规范中:

subject { described_class.new(arg1, view_context) }
let(:view_context) { ViewContextDelegator.new }

或者,如果仅需要用于测试目的,您可以定义一个测试助手。

def view_context
  @view_context ||= ApplicationController
                    .helpers
                    .extend(Module.new { delegate_missing_to 'Rails.application.routes.url_helpers' })
end

0

根据这个答案,以下(稍作修改)对我有用:

  1. 将以下内容添加到rails_helper.rb
config.include ActionView::TestCase::Behavior, file_path: %r{spec/presenters}

(假设您的演示文稿类位于/presenters文件夹中)

  1. 完成上述步骤后,您可以通过view方法在规范中访问视图上下文:
FoobarPresenter.new(Foobar.new, view)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接