Many Rails developers who are new to writing tests struggle with the question of what to write tests for and how.
I’m about to share with you a repeatable formula that you can use to write an integration test for almost any Rails feature. It’s nothing particularly profound, it won’t result in 100% test coverage, and it won’t work in all cases, but it will certainly get you started if you’re stuck.
The three integration test cases I write for any Rails CRUD feature
Most features in most Rails applications are, for better or worse, some slight variation on CRUD operations. Here are the tests I almost always write for every CRUD feature.
- Creating a record with valid inputs
- Trying to create a record with invalid inputs
- Updating a record
Let me go into detail on each one of these. For each test case, let’s imagine we’re working with a resource called Location
.
Creating a record with valid inputs
This test case will involve the following steps:
- If necessary, sign a user in (using
login_as(create(:user))
as I describe in this post) - Visit the “new” route for the resource (e.g.
visit new_location_path
) - Fill out the form fields using
fill_in
,select
, etc. - Click the “submit” button
- Expect that the page has whatever content the resource’s index page has, e.g.
expect(page).to have_content('Locations')
Here’s what that test might look like:
require 'rails_helper'
RSpec.describe 'Creating a location', type: :system do
before do
login_as(create(:user))
create(:state, name: 'Michigan')
visit new_location_path
end
scenario 'valid inputs' do
fill_in 'Name', with: "Jason's House"
fill_in 'Line 1', with: '69420 Cool Ave'
fill_in 'City', with: 'Sand Lake'
select 'Michigan', from: 'State'
fill_in 'Zip code', with: '49343'
click_on 'Save Location'
expect(page).to have_content('Locations')
end
end
Trying to create a record with invalid inputs
This test is pretty simple because all I have to do is not fill out the form. So the steps are:
- If necessary, sign a user in (using
login_as(create(:user))
- Visit the “new” route for the resource (e.g.
visit new_location_path
) - Click the “submit” button
- Expect that the page shows an error (e.g.
expect(page).to have_content("Name can't be blank")
)
Here’s what a test of this type might look like.
require 'rails_helper'
RSpec.describe 'Creating a location', type: :system do
before do
login_as(create(:user))
create(:state, name: 'Michigan')
visit new_location_path
end
scenario 'invalid inputs' do
click_on 'Save Location'
expect(page).to have_content("Name can't be blank")
end
end
Updating a record
The steps for this one go:
- If necessary, sign a user in (using
login_as(create(:user))
- Create an instance of the resource I’m testing (e.g.
location = create(:location)
) - Visit the “edit” route for the resource (e.g.
visit edit_location_path(location)
) - Fill in just one field with a different value
- Click the “submit” button
- Expect that the page has whatever content the resource’s index page has, e.g.
expect(page).to have_content('Locations')
require 'rails_helper'
RSpec.describe 'Updating a location', type: :system do
before do
login_as(create(:user))
location = create(:location)
visit edit_location_path(location)
end
scenario 'valid inputs' do
fill_in 'Name', with: "Jason's Filthy Shack"
click_on 'Save Location'
expect(page).to have_content('Locations')
end
end
“But is that enough? Don’t I need more?”
No, this is not “enough”, and yes, you do need more…eventually. What I’m hoping to share with you here is a base that you can start with if you’re totally lost. Writing halfway-good tests is better than writing no tests, and even writing crappy tests is better than writing no tests. Once you get some practice following the formula above, you can expand on that formula to get a better level of test coverage.
Where to go next
If you get some practice with writing tests like the above and you want to go further, you might like my RSpec/Capybara integration test tutorial.