If you’re like me, you might have found the difference between RSpec’s “feature specs” and “system specs” to be a little too nuanced to be easily understandable. Here’s my explanation of the difference between the two.
Two levels of Rails tests
For some background, I want to talk about the main two types of Rails tests that I use, and where feature specs, the predecessor to system specs, come intro the picture.
I think of Rails tests as existing on two basic levels.
One level of tests is at a high level. These tests simulate a user visiting pages, filling in forms, clicking links and buttons, etc. Different people use different terms for these tests including “integration test”, “end-to-end test” and “acceptance test”.
Because these types of tests are often expensive to write and run, they tend not to cover every nook and cranny of the application’s behavior.
In RSpec terminology, these types of tests have historically been called feature specs.
The other level of tests is at a lower level. There are all kinds of tests you could possibly write with Rails/RSpec (model specs, request specs, view specs, helper specs) but I tend to skip most of those in most scenarios and only write model specs.
From feature spec to system spec
When Rails 5.1 came out in April 2017, one of the features it introduced was system tests. Here’s why this was done.
By default Rails ships with Minitest. The inclusion of Minitest provides a way to write model tests and certain other kinds of tests, but it historically hasn’t provided a way to write full-blown end-to-end without doing some extra work yourself, like bringing Capybara and Database Cleaner into the picture. This is the rationale for the addition of system tests, in my understanding.
System specs wrap system tests
According to the RSpec docs, “System specs are RSpec’s wrapper around Rails’ own system tests.” This means that it’s no longer required to explicitly include the Capybara gem, and because system tests are already run inside a transaction, you don’t need Database Cleaner.
The syntactical difference
(Note: it’s been pointed out to me by Reddit user jrochkind that the “feature/scenario” and “describe/it” syntaxes are interchangeable and independent of system specs vs. features specs. Apparently the RSpec team has developed a preference for the “describe/it” version.)
Feature spec example
require "rails_helper" RSpec.feature "Widget management", :type => :feature do scenario "User creates a new widget" do visit "/widgets/new" fill_in "Name", :with => "My Widget" click_button "Create Widget" expect(page).to have_text("Widget was successfully created.") end end
System spec example
require "rails_helper" RSpec.describe "Widget management", :type => :system do it "enables me to create widgets" do visit "/widgets/new" fill_in "Name", :with => "My Widget" click_button "Create Widget" expect(page).to have_text("Widget was successfully created.") end end
System specs are a wrapper around Rails’ system tests. The benefits of using system specs instead of feature specs is that you don’t have to explicitly include the Capybara gem, nor do you have to use Database Cleaner. If you prefer the new syntax (as I do) which more closely matches the RSpec syntax for all the other RSpec spec types, then that’s an additional benefit as well.