使用RSpec和Rails测试给定布局的渲染

是否可以使用带有Rails的RSpec测试给定布局的使用,例如我想要一个执行以下操作的匹配器:

response.should use_layout('my_layout_name') 

我在谷歌搜索时找到了一个use_layout匹配器,但它不起作用,因为响应或控制器似乎都没有匹配器正在寻找的布局属性。

我找到了一个如何编写use_layout匹配器的例子。 这是链接消失的代码:

 # in spec_helper.rb class UseLayout def initialize(expected) @expected = 'layouts/' + expected end def matches?(controller) @actual = controller.layout #@actual.equal?(@expected) @actual == @expected end def failure_message return "use_layout expected #{@expected.inspect}, got # {@actual.inspect}", @expected, @actual end def negeative_failure_message return "use_layout expected #{@expected.inspect} not to equal # {@actual.inspect}", @expected, @actual end end def use_layout(expected) UseLayout.new(expected) end # in controller spec response.should use_layout("application") 

David Chelimsky在Ruby论坛上发表了一个很好的答案:

 response.should render_template("layouts/some_layout") 

这适用于Rails上的边缘Rails和边缘RSpec:

 response.layout.should == 'layouts/application' 

不应该把它变成适合你的匹配器。

已经有一个完美的function匹配器:

 response.should render_template(:layout => 'fooo') 

(Rspec 2.6.4)

我必须编写以下内容才能完成这项工作:

 response.should render_template("layouts/some_folder/some_layout", "template-name") 

这是匹配器的更新版本。 我已经更新它以符合最新版本的RSpec。 我添加了相关的只读属性并删除了旧的返回格式。

 # in spec_helper.rb class UseLayout attr_reader :expected attr_reader :actual def initialize(expected) @expected = 'layouts/' + expected end def matches?(controller) if controller.is_a?(ActionController::Base) @actual = 'layouts/' + controller.class.read_inheritable_attribute(:layout) else @actual = controller.layout end @actual ||= "layouts/application" @actual == @expected end def description "Determines if a controller uses a layout" end def failure_message return "use_layout expected #{@expected.inspect}, got #{@actual.inspect}" end def negeative_failure_message return "use_layout expected #{@expected.inspect} not to equal #{@actual.inspect}" end end def use_layout(expected) UseLayout.new(expected) end 

此外,匹配器现在还可以使用在控制器类级别指定的布局,并可以按如下方式使用:

 class PostsController < ApplicationController layout "posts" end 

在控制器规范中,您可以简单地使用:

 it { should use_layout("posts") } 

这是我最终选择的解决方案。 它用于rpsec 2和rails 3。
我刚刚在spec / support目录中添加了这个文件。 链接是: https : //gist.github.com/971342

 # spec/support/matchers/render_layout.rb 

# spec/support/matchers/render_layout.rb

ActionView :: Base.class_eval做
除非instance_methods.include?(’_ render_layout_with_tracking’)
def _render_layout_with_tracking(layout,locals,&block)
controller.instance_variable_set(:@_ rendered_layout,layout)
_render_layout_without_tracking(布局,本地和块)
结束
alias_method_chain:_render_layout,:tracking
结束
结束

#您可以在有权访问控制器实例的任何地方使用此匹配器,
#喜欢在控制器或集成规范中。

#==示例用法

#预计不会呈现任何布局:
#controller.should_not render_layout
#预计要呈现的任何布局:
#controller.should render_layout
#预计要呈现的app / views / layouts / application.html.erb:
#controller.should render_layout(’application’)
#预计不会呈现app / views / layouts / application.html.erb:
#controller.should_not render_layout(’application’)
#预计要呈现的app / views / layouts / mobile / application.html.erb:
#controller.should_not render_layout(’mobile / application’)
RSpec :: Matchers.define:render_layout do | * args |
expected = args.first
匹配做| c |
actual = get_layout(c)
如果expect.nil?
!actual.nil? #real必须为nil才能通过测试。 用法:should_not render_layout
elsif实际
实际== expected.to_s
其他

结束
结束

failure_message_for_should做| c |
actual = get_layout(c)
if actual.nil? && expected.nil?
“预计会有一个布局,但没有一个是”
elsif actual.nil?
“预期布局#{expected.inspect}但没有呈现布局”
其他
“预期布局#{expected.inspect},但#{actual.inspect}已呈现”
结束
结束

failure_message_for_should_not do | c |
actual = get_layout(c)
如果expect.nil?
“预计没有布局,但#{actual.inspect}已呈现”
其他
“预期#{expected.inspect}不会被渲染,但它是”
结束
结束

def get_layout(控制器)
if template = controller.instance_variable_get(:@_ rendered_layout)
template.virtual_path.sub(/ layouts \ //,”)
结束
结束
结束

response.should render_template("layouts/some_folder/some_layout") response.should render_template("template-name")

controller.active_layout.name适合我。

这是dmcnally代码的一个版本,它不允许传递任何参数,使“should use_layout”和“should_not use_layout”工作(断言控制器分别使用任何布局,或者没有布局 – 我只期望第二个是有用的,因为如果它使用布局,你应该更具体):

 class UseLayout def initialize(expected = nil) if expected.nil? @expected = nil else @expected = 'layouts/' + expected end end def matches?(controller) @actual = controller.layout #@actual.equal?(@expected) if @expected.nil? @actual else @actual == @expected end end def failure_message if @expected.nil? return 'use_layout expected a layout to be used, but none was', 'any', @actual else return "use_layout expected #{@expected.inspect}, got #{@actual.inspect}", @expected, @actual end end def negative_failure_message if @expected.nil? return "use_layout expected no layout to be used, but #{@actual.inspect} found", 'any', @actual else return "use_layout expected #{@expected.inspect} not to equal #{@actual.inspect}", @expected, @actual end end end def use_layout(expected = nil) UseLayout.new(expected) end 

Shoulda Matchers为此场景提供了匹配器。 ( 文档 )这似乎有效:

  expect(response).to render_with_layout('my_layout') 

它产生适当的失败消息,如:

预计使用“calendar_layout”布局进行渲染,但使用“application”,“application”进行渲染

使用rails 4.2rspec 3.3shoulda-matchers 2.8.0

编辑:shoulda-matchers提供此方法。 早该::匹配器:: ActionController的:: RenderWithLayoutMatcher