Testing rendering of a given layout with RSpec & Rails

后端 未结 11 1384
滥情空心
滥情空心 2020-12-24 12:31

Is it possible to test the use of a given layout using RSpec with Rails, for example I\'d like a matcher that does the following:

response.should use_layout(         


        
相关标签:
11条回答
  • 2020-12-24 12:56

    Here is an updated version of the matcher. I've updated it to conform to the latest version of RSpec. I've added the relevant read only attributes and remove old return format.

    # 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
    

    Additionally the matcher now also works with layouts specified at the controller class level and can be used as follows:

    class PostsController < ApplicationController
      layout "posts"
    end
    

    And in the controller spec you can simply use:

    it { should use_layout("posts") }
    
    0 讨论(0)
  • 2020-12-24 12:56

    Shoulda Matchers provides a matcher for this scenario. (Documentation) This seems to work:

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

    it produces appropriate failure messages like:

    Expected to render with the "calendar_layout" layout, but rendered with "application", "application"

    Tested with rails 4.2, rspec 3.3 and shoulda-matchers 2.8.0

    Edit: shoulda-matchers provides this method. Shoulda::Matchers::ActionController::RenderWithLayoutMatcher

    0 讨论(0)
  • 2020-12-24 12:57

    I found an example of how to write a use_layout matcher that will do just that. Here's the code in case that link goes away:

    # 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")
    
    0 讨论(0)
  • 2020-12-24 12:59

    This works for me with edge Rails and edge RSpec on Rails:

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

    Shouldn't be hard to turn this into a matcher suitable for you.

    0 讨论(0)
  • 2020-12-24 13:02

    Here's a version of dmcnally's code that allows no arguments to be passed, making "should use_layout" and "should_not use_layout" work (to assert that the controller is using any layout, or no layout, respectively - of which I would expect only the second to be useful as you should be more specific if it is using a 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
    
    0 讨论(0)
  • 2020-12-24 13:06

    David Chelimsky posted a good answer over on the Ruby Forum:

    response.should render_template("layouts/some_layout")
    
    0 讨论(0)
提交回复
热议问题