Test du rendu d'une mise en page donnée avec RSpec & amp; Des rails
-
01-07-2019 - |
Question
Est-il possible de tester l'utilisation d'une présentation donnée à l'aide de RSpec avec Rails, par exemple, j'aimerais un matcher qui effectue les opérations suivantes:
response.should use_layout('my_layout_name')
J'ai trouvé un matcher use_layout lors de la recherche sur Google, mais cela ne fonctionne pas, car ni la réponse ni le contrôleur ne semblent avoir une propriété de disposition recherchée par matcher.
La solution
J'ai trouvé un exemple de comment écrire un use_layout
matcher qui fera exactement cela. Voici le code au cas où ce lien disparaîtrait:
# 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")
Autres conseils
David Chelimsky a publié une bonne réponse sur le Ruby Forum :
response.should render_template("layouts/some_layout")
Cela fonctionne pour moi avec Edge Rails et Edge RSpec on Rails:
response.layout.should == 'layouts/application'
Ne devrait pas être difficile de transformer cela en un ajusteur qui vous convient.
Il existe déjà un matcher parfaitement fonctionnel pour cela:
response.should render_template(:layout => 'fooo')
(Rspec 2.6.4)
J'ai dû écrire ce qui suit pour que cela fonctionne:
response.should render_template("layouts/some_folder/some_layout", "template-name")
Voici une version mise à jour du matcher. Je l'ai mis à jour pour qu'il soit conforme à la dernière version de RSpec. J'ai ajouté les attributs pertinents en lecture seule et supprimé l'ancien format de retour.
# 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
De plus, le matcher fonctionne également avec les présentations spécifiées au niveau de la classe du contrôleur et peut être utilisé comme suit:
class PostsController < ApplicationController
layout "posts"
end
Et dans la spécification du contrôleur, vous pouvez simplement utiliser:
it { should use_layout("posts") }
Voici la solution avec laquelle j'ai fini par aller. C'est pour rpsec 2 et rails 3.
Je viens d'ajouter ce fichier dans le répertoire spec / support.
Le lien est le suivant: 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
response.should render_template (" layouts / some_folder / some_layout ")
response.should render_template (" nom-modèle ")
controller.active_layout.name
fonctionne pour moi.
Voici une version du code de dmcnally qui ne permet de passer aucun argument, ce qui permet à "devrait utiliser use_layout". et " ne devrait pas utiliser_layout " work (pour affirmer que le contrôleur utilise une mise en page, ou aucune mise en page, respectivement - dont je m'attendrais seulement à ce que la seconde soit utile car vous devriez être plus précis s'il utilise une mise en page):
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
Les correspondants d’Ina fournissent un indicateur pour ce scénario. ( Documentation ) Cela semble fonctionner:
expect(response).to render_with_layout('my_layout')
il génère les messages d'échec appropriés, tels que:
Est attendu pour le rendu avec le "quot_ calendar_layout" mise en page, mais rendue avec "application", "application"
Testé avec rails 4.2
, rspec 3.3
et shoulda-matchers 2.8.0
Edit: shoulda-matchers fournit cette méthode. Shoulda :: Matchers :: ActionController :: RenderWithLayoutMatcher