Read more

RSpec: Leverage the power of Capybara Finders and Matchers for view specs

Felix Eschey
October 24, 2023Software engineer at makandra GmbH

View specs Show archive.org snapshot are a powerful tool to test several rendering paths by their cases instead of using a more costing feature spec. This is especially useful because they become quite convenient when used with Capybara::Node::Finders Show archive.org snapshot and Capybara::RSpecMatchers Show archive.org snapshot . This allows within view specs to isolate specific parts of the rendered view and then use readable and powerful matchers on that part.

How to use

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

You can simply create a Capybara::Node::Simple object from the rendered string in your view spec by following the approach shown in the linked article Validating Views with Capybara Queries Show archive.org snapshot .

As taken from the linked article you can do:

it 'does have a nav link to something' do
  ... # assign and render a view or template

  doc = Capybara.string(rendered)
  expect(doc).to have_no_css('nav a', text: 'Something')
end

Note that this will allow you to use Capybara Finders methods (e.g. #find) on the doc element and then call matchers on that element:

link_element = doc.find('a')
expect(link_element).to have_selector('.nav-link')

Info

Even though require 'capybara/rspec' includes Capybara::RSpecMatchers by default, such that rendered is converted once one of Capybara's matchers is called on it, it's default type is still a String. (As noted in the Capybara doc Show archive.org snapshot )

So if you also want to user Capybara's finders, you still will have to convert these by yourself.

Enhanced style for view specs

  • This approach allows your view specs to be styled with enhanced readability by isolating the relevant part of the page within a subject block.
  • You can also use describe to specify which parts of your html you are currently interested in naming it with it's actual selector.
  • The context can be used to hold information on the current state of the view render

Example

Let's say we have a view which decides to render a link to the associated user or a just text of it's name depending on his soft deleted state.

describe 'movies/show' do
  describe '.content--attribute.-for-user' do
    subject(:rendered_attribute) do
      capybara_node = Capybara.string(rendered)
      capybara_node.find('p.content--attribute.-for-user')
    end

    context 'when the creator of the movie is not soft deleted' do
      it 'renders the creators name as a link to his show view' do
        # create movie and associated user 'Carl Gustav'
        
        assign(:movie, movie)
        render
        
        expect(rendered_attribute).to have_selector("a[href=\"#{user_path(user)}\"]", text: 'Carl Gustav')
      end
    end
    
    context 'when the creator of the movie is soft deleted' do
      # spec that the user is displayed as text only
    end 
  end
end

Limitations

A general downside with these specs is, that your are not going through the controller by using the routes as you would with request specs. This means that you are creating a faked state on your view template, which might not be possible to create in a real scenario.

So prefer to use these kind of specs for rather simple cases, since complex states within your app might change while you will might not notice this on your view specs!

Concerning authorization

Especially using view-specs for authorization-dependent if-else-cases in your view might be too isolated, since view-specs will mock a lot of rails behavior and render the view independent from the controller-logic.

Therefore it will be more applicable to test views within request specs. But, even then, you still can use Caypbara's matchers within request-specs by including them separetly for request specs:

RSpec.configure do |config|
  config.include Capybara::RSpecMatchers, type: :request
end
Felix Eschey
October 24, 2023Software engineer at makandra GmbH
Posted by Felix Eschey to makandra dev (2023-10-24 08:17)