Robert Eshleman

web developer in New York

Readable RSpec Tests With Helper Methods

| Comments

I’m a recent graduate of Metis, a 12-week Ruby on Rails course taught by some great folks from thoughtbot. This post is part of a series sharing my experience and some of the things I’ve learned.

I’ve been spending a lot of time recently becoming familiar with Rspec, Capybara, and TDD. Because tests frequently have similar or repeated steps, this has also been a great opportunity to practice DRY-ing up my code.

One particularly effective technique I’ve been using often in my tests is extracting helper methods with descriptive names to make my code easier to read.

Setup and Exercise

In the setup and exercise phases, code across tests may be very similar and ripe for extraction into a method. For example, here’s a helper method in one of my feature specs for a user signing in:

1
2
3
4
def visit_sign_in_page
  visit root_path
  click_link "Sign in"
end

And another simple one that takes some parameters:

1
2
3
4
5
def user_signs_in_as(email, password)
  fill_in "Email", with: email
  fill_in "Password", with: password
  click_button "Sign in"
end

Verify

I’ve found that the verification phase of my tests tend to see the most readability benefit from extracting methods.

For example, this was some code that I used in a test verification recently:

1
expect(page).to have_css(".event", text: event.name)

This isn’t too bad, but it becomes clearer when extracted into a helper method:

1
2
3
def have_event_summary(event)
  have_css(".event", text: event.name)
end

The intent of this verification is obvious when I now write expect(page).to have_event_summary(event) or expect(page).not_to have_event_summary(event).

For more complex verifications, the expectation can also be included in the helper method, like so:

1
2
3
4
5
6
def expect_page_to_have_user_review_summary_for(event)
  within(".review-score", text: "Users") do
    expect(page).to have_text(event.average_user_review_score)
    expect(page).to have_text("#{event.num_user_reviews} Reviews")
  end
end

The verification phase for most of my tests using this method simply reads expct_page_to_have_user_review_summary_for(event). This is much nicer than multiple have_css or have_text expectations.

As an additional bonus, RSpec will even provide clear failure messages if these assertions fail:

1
2
Failure/Error: expect_page_to_have_user_review_summary_for(event)
expected to find text "9.0" in "No Review Here"

Reuse Across Files

Finally, RSpec allows these methods to be easily reused across spec files, if we so desire.

All we have to do is wrap them up in a module like EventSummaryHelpers in spec/support/. Then, in spec/rails_helper.rb, simply tell RSpec to load that module:

1
2
3
RSpec.configure do |config|
  config.include EventSummaryHelpers, type: :feature
end

In this case, to save overhead, RSpec will only load these helpers when running feature specs.

Resources

Comments