使用 RSpec & Rails 测试给定布局的渲染
Posted
技术标签:
【中文标题】使用 RSpec & Rails 测试给定布局的渲染【英文标题】:Testing rendering of a given layout with RSpec & Rails 【发布时间】:2010-09-11 16:15:56 【问题描述】:是否可以使用带有 Rails 的 RSpec 来测试给定布局的使用,例如,我想要一个执行以下操作的匹配器:
response.should use_layout('my_layout_name')
我在谷歌搜索时发现了一个 use_layout 匹配器,但它不起作用,因为响应或控制器似乎都没有匹配器正在寻找的布局属性。
【问题讨论】:
【参考方案1】:David Chelimsky 在Ruby Forum 上发布了一个很好的答案:
response.should render_template("layouts/some_layout")
【讨论】:
@Kevin Ansfield:但问题是关于布局而不是模板。 @p.matsinopoulos 布局只是专门的模板,它们仍然会被渲染,因此您可以检查【参考方案2】:这对我来说适用于边缘 Rails 和边缘 RSpec on Rails:
response.layout.should == 'layouts/application'
应该不难把它变成适合你的匹配器。
【讨论】:
这在较新版本的 rspec 中不起作用。而是使用response.should render_template('layouts/the_layout")
【参考方案3】:
已经有一个功能完善的匹配器:
response.should render_template(:layout => 'fooo')
(Rspec 2.6.4)
【讨论】:
这给了我一个误报,即“response.should render_template(layout: false)”它通过了,即使我有一个布局。 为我工作正确,但需要使用response.should render_template(:layout => 'layouts/fooo')
。没有观察到假阳性。【参考方案4】:
我找到了一个how to write a use_layout
matcher 的例子,它可以做到这一点。这是链接消失时的代码:
# 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")
【讨论】:
这与我发现的相同,从我的测试来看,它似乎不起作用,因为我所看到的响应(或控制器)上没有布局属性。 啊,好像有一个布局方法,但是需要一些参数。我会思考一下解决方案可能是什么。【参考方案5】:我必须编写以下代码才能完成这项工作:
response.should render_template("layouts/some_folder/some_layout", "template-name")
【讨论】:
【参考方案6】:这是匹配器的更新版本。我已经更新它以符合最新版本的 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")
【讨论】:
【参考方案7】:这是我最终采用的解决方案。它适用于 rpsec 2 和 rails 3。 我刚刚在 spec/support 目录中添加了这个文件。 链接为:https://gist.github.com/971342
# spec/support/matchers/render_layout.rb
ActionView::Base.class_eval do
unless 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(layout, locals, &block)
end
alias_method_chain :_render_layout, :tracking
end
end
# You can use this matcher anywhere that you have access to the controller instance,
# like in controller or integration specs.
#
# == Example Usage
#
# Expects no layout to be rendered:
# controller.should_not render_layout
# Expects any layout to be rendered:
# controller.should render_layout
# Expects app/views/layouts/application.html.erb to be rendered:
# controller.should render_layout('application')
# Expects app/views/layouts/application.html.erb not to be rendered:
# controller.should_not render_layout('application')
# Expects app/views/layouts/mobile/application.html.erb to be rendered:
# controller.should_not render_layout('mobile/application')
RSpec::Matchers.define :render_layout do |*args|
expected = args.first
match do |c|
actual = get_layout(c)
if expected.nil?
!actual.nil? # actual must be nil for the test to pass. Usage: should_not render_layout
elsif actual
actual == expected.to_s
else
false
end
end
failure_message_for_should do |c|
actual = get_layout(c)
if actual.nil? && expected.nil?
"expected a layout to be rendered but none was"
elsif actual.nil?
"expected layout #expected.inspect but no layout was rendered"
else
"expected layout #expected.inspect but #actual.inspect was rendered"
end
end
failure_message_for_should_not do |c|
actual = get_layout(c)
if expected.nil?
"expected no layout but #actual.inspect was rendered"
else
"expected #expected.inspect not to be rendered but it was"
end
end
def get_layout(controller)
if template = controller.instance_variable_get(:@_rendered_layout)
template.virtual_path.sub(/layouts\//, '')
end
end
end
【讨论】:
在 Rails 3 中,这个解决方案对我有用(参见 get_layout 方法)【参考方案8】:
response.should render_template("layouts/some_folder/some_layout")
response.should render_template("template-name")
【讨论】:
【参考方案9】:controller.active_layout.name
为我工作。
【讨论】:
【参考方案10】:应该匹配器为此场景提供了匹配器。 (Documentation) 这似乎有效:
expect(response).to render_with_layout('my_layout')
它会产生适当的失败消息,例如:
预期使用“calendar_layout”布局呈现,但使用“application”、“application”呈现
使用rails 4.2
、rspec 3.3
和shoulda-matchers 2.8.0
测试
编辑:shouda-matchers 提供了这个方法。应该::Matchers::ActionController::RenderWithLayoutMatcher
【讨论】:
我使用的是RSpec 3.3,它根本不起作用,RSpec中没有这样的方法。 你是对的。我查看了该方法的来源,它实际上来自于 Shoulda::Matchers::ActionController::RenderWithLayoutMatcher (v.2.8.0) rubydoc.info/github/thoughtbot/shoulda-matchers/… 我会更新这个。【参考方案11】:这是 dmcnally 代码的一个版本,它不允许传递任何参数,使“应该使用布局”和“应该不使用布局”工作(分别断言控制器正在使用任何布局或没有布局 - 我希望只有第二个有用,因为如果它使用布局,您应该更具体):
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
【讨论】:
以上是关于使用 RSpec & Rails 测试给定布局的渲染的主要内容,如果未能解决你的问题,请参考以下文章
.env 未使用 rspec 在 Rails 的测试环境中加载