A repeatable, step-by-step process for writing Rails integration tests with Capybara

by Jason Swett,

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.

  1. Creating a record with valid inputs
  2. Trying to create a record with invalid inputs
  3. 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:

  1. If necessary, sign a user in (using login_as(create(:user)) as I describe in this post)
  2. Visit the “new” route for the resource (e.g. visit new_location_path)
  3. Fill out the form fields using fill_in, select, etc.
  4. Click the “submit” button
  5. 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:

  1. If necessary, sign a user in (using login_as(create(:user))
  2. Visit the “new” route for the resource (e.g. visit new_location_path)
  3. Click the “submit” button
  4. 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:

  1. If necessary, sign a user in (using login_as(create(:user))
  2. Create an instance of the resource I’m testing (e.g. location = create(:location))
  3. Visit the “edit” route for the resource (e.g. visit edit_location_path(location))
  4. Fill in just one field with a different value
  5. Click the “submit” button
  6. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *