Read more

Don't assert exceptions in feature specs

Michael Leimstädtner
July 11, 2023Software engineer at makandra GmbH

As we are slowly switching from Cucumber scenarios to RSpec feature specs, you might be tempted to write assertions like this one:

feature 'authorization for cards management' do
  let(:guest_user) { create(:user, :guest) }

  scenario "rejects guest users from adding new cards", js: true do
    sign_in guest_user

    expect { visit new_cards_path }.to raise_error(Consul::Powerless)
  end
end
Illustration book lover

Growing Rails Applications in Practice

Check out our e-book. Learn to structure large Ruby on Rails codebases with the tools you already know and love.

  • Introduce design conventions for controllers and user-facing models
  • Create a system for growth
  • Build applications to last
Read more Show archive.org snapshot

While this might work under certain circumstances¹, there is a good chance you'll see two exceptions when running this single spec:

  • expected Consul::Powerless but nothing was raised
  • No power to ['creatable_cards']

The reason for this behavior is that the Capybara test server is running in another thread, and the RSpec thread can't "see" the exception at this point in time.
As we are emulating a user interacting with the Browser, you should not write such assertions in feature specs anyway. Instead, you could:

  • write the same test as a request spec
  • expect what the user sees in this situation, e.g. an error flash.

¹

  • Rack::Test features emulate the server within the same thread and will therefore "see" those exceptions
  • The test thread and server thread of a JS-spec actually both have access to the last occured exception, but only before the next interaction. So technically you could add a line like visit new_cards_path after sign_in in the example above to make it green.
Posted by Michael Leimstädtner to makandra dev (2023-07-11 09:04)