Refactoring an integration test

I am trying to refactor (increase the DRYness) of the below test and I’m not quite sure how to do it using the given feature/scenario syntax. Any help would be appreciated.

require 'spec_helper'

feature 'Sign in as a user' do
 let(:user) { Fabricate(:user) }
  scenario 'with an email address' do
    visit sign_in_path
    fill_in 'email', with: user.email
    fill_in 'password', with: 'password'
    click_button 'sign in'
    expect(page).to have_content "Welcome, #{user.first_name}"
  end
  scenario 'with invalid credentials' do
    visit sign_in_path
    fill_in 'email', with: 'some@email.com'
    fill_in 'password', with: 'password'
    click_button 'sign in'
    expect(page).to have_content "Email or password was invalid" 
  end
end

feature 'Sign out as a user' do
  let(:user) { Fabricate(:user) }
  scenario 'I am logged in and want to sign out' do
    visit sign_in_path
    fill_in 'email', with: user.email
    fill_in 'password', with: 'password'
    click_button 'sign in'
    click_link 'sign out'
    expect(page).to have_content 'sign in'
  end
end

The repeated section that stands out to me as a great candidate for extraction is this:

visit sign_in_path
fill_in 'email', with: …
fill_in 'password', with: …
click_button 'sign in'

If that were extracted into a method your specs would be much shorter:

feature 'Sign in as a user' do
 let(:user) { Fabricate(:user) }
  scenario 'with an email address' do
    sign_in_with user.email, 'password'
    expect(page).to have_content "Welcome, #{user.first_name}"
  end
  scenario 'with invalid credentials' do
    sign_in_with 'some@email.com', 'password'
    expect(page).to have_content "Email or password was invalid" 
  end
end

feature 'Sign out as a user' do
  let(:user) { Fabricate(:user) }
  scenario 'I am logged in and want to sign out' do
    sign_in_with user.email, 'password'
    click_link 'sign out'
    expect(page).to have_content 'sign in'
  end
end

The only question is, where does this new sign_in_with method go?

RSpec lets you write helper modules which are included in your feature or describe groups. You can also specify which type of specs each helper module is included in:

  1. Since this is a feature spec, let’s call the helper module Features and put it in spec/support/features.rb:

     module Features
       def sign_in_with(email, password)
         # …
       end
     end
    
  2. Include that helper module in all feature specs by adding some config to spec/spec_helper.rb:

     RSpec.configure do |config|
       config.include Features, type: :feature
       # …
     end
    

As well as DRYing up your existing spec, putting this method in a helper module makes it available to future specs too.

1 Like

Normally I just “like” good answers, but this one was exceptional and exactly what I wanted. I knew the Wetness of my test, I just wasn’t sure how to extract it out. Thanks so much!