Probar la representación de un diseño determinado con RSpec & Rails
-
01-07-2019 - |
Pregunta
¿Es posible probar el uso de un diseño determinado usando RSpec con Rails? Por ejemplo, me gustaría un comparador que haga lo siguiente:
response.should use_layout('my_layout_name')
Encontré un comparador use_layout cuando busqué en Google, pero no funciona porque ni la respuesta ni el controlador parecen tener una propiedad de diseño que el comparador estaba buscando.
Solución
Encontré un ejemplo de cómo escribir un use_layout
igualador eso hará precisamente eso.Aquí está el código en caso de que el enlace desaparezca:
# 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")
Otros consejos
David Chelimsky publicó una buena respuesta en el Foro Rubí:
response.should render_template("layouts/some_layout")
Esto funciona para mí con Edge Rails y Edge RSpec on Rails:
response.layout.should == 'layouts/application'
No debería ser difícil convertir esto en un comparador adecuado para usted.
Ya existe un comparador perfectamente funcional para esto:
response.should render_template(:layout => 'fooo')
(Rspec 2.6.4)
Tuve que escribir lo siguiente para que esto funcione:
response.should render_template("layouts/some_folder/some_layout", "template-name")
Aquí hay una versión actualizada del comparador.Lo actualicé para que se ajuste a la última versión de RSpec.Agregué los atributos relevantes de solo lectura y eliminé el formato de devolución anterior.
# 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
Además, el comparador ahora también funciona con diseños especificados en el nivel de clase del controlador y se puede utilizar de la siguiente manera:
class PostsController < ApplicationController
layout "posts"
end
Y en las especificaciones del controlador simplemente puedes usar:
it { should use_layout("posts") }
Aquí está la solución con la que terminé.Es para rpsec 2 y rieles 3.
Acabo de agregar este archivo en el directorio spec/support.El enlace es: https://gist.github.com/971342
# spec/support/matchers/render_layout.rb
ActionView :: Base.class_eval do a menos que instance_methods.include? ('_ Render_layout_with_tracking') def _render_layout_with_tracking (lugar _chain: _render_layout,: End de seguimiento
# Puede usar este Matcher en cualquier lugar donde tenga acceso a la instancia del controlador, # como en el controlador o las especificaciones de integración.# == Ejemplo Uso # # espera que no se represente el diseño:# controlador.should_not render_layout # espera que se represente cualquier diseño:# controlador. Debería render_layout # espera que la aplicación/vistas/diseños/application.html.erb se represente:# controlador.sho debería render_layout ('aplicación') # espera que la aplicación/vistas/diseños/application.html.erb no se represente:# controlador.should_not render_layout ('aplicación') # espera que la aplicación/vistas/diseños/móvil/application.html.erb se represente:# controlador.should_not render_layout ('mobile/aplicación') rspec :: matchers.define: render_layout do |*args | esperado = args. primero coincidora do | c | real = get_layout (c) si se espera.nil?!actual.nulo?# real debe ser nulo para que se pase la prueba.Uso:debería_not render_layout elsif real real == esperado.to_s más falso final final
fails_message_for_shho debería hacer | c | real = get_layout (c) si real.nil?&& esperado.nil?"Se espera que se represente un diseño pero ninguno era" Elsif real. ¿Nil?"Diseño esperado #{esperado.inspect} Pero no se presentó un diseño" más "el diseño esperado #{esperado.inspect} pero #{real.inspect} se renderizó" Fin final
fails_message_for_should_not do | c | real = get_layout (c) si se espera.nil?"No esperado sin diseño, pero #{real.inspect} se convirtió en" else "esperado #{esperado.inspect} para no ser presentado, pero fue" final
def get_layout(controller) if template = controller.instance_variable_get(:@_rendered_layout) template.virtual_path.sub(/layouts\//, '') end end end
response.should render_template("layouts/some_folder/some_layout")
response.should render_template("template-name")
controller.active_layout.name
funciona para mi.
Aquí hay una versión del código de dmcnally que no permite pasar argumentos, lo que hace que funcionen "debería usar_diseño" y "no debería usar_diseño" (para afirmar que el controlador está usando cualquier diseño, o ningún diseño, respectivamente, de lo cual esperaría solo el segundo para que sea útil, ya que debería ser más específico si utiliza un diseño):
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
Coincidencias de deberías proporciona un comparador para este escenario.(Documentación) Esto parece funcionar:
expect(response).to render_with_layout('my_layout')
Produce mensajes de error apropiados como:
Se espera que se represente con el diseño "calendar_layout", pero se representa con "aplicación", "aplicación"
Probado con rails 4.2
, rspec 3.3
y shoulda-matchers 2.8.0
Editar:debería-matchers proporciona este método.Debería::Matchers::ActionController::RenderWithLayoutMatcher