Rails + RSpec + Turnip = <3

Reading Time: 4 minutes

Hi there! While working on our current project, we decided to add some integration testing (in the past we used to have only API endpoints, no UI). After doing some research, a colleague found the Turnip gem.

The team had been using Cucumber for a long time now, but we wanted to look for a new approach. Once we saw a thoughtbot post and checked the Turnip repo, we decided to give it a chance and try it out — especially because it’s claimed that Turnip works by loading the Rails server just once and is mounted directly to RSpec, which makes it faster than Cucumber.

Why did we decide to use Turnip?

We use continuous delivery to send new changes to production. That means that every single PR is reviewed by the engineers, making sure that all tests are in the green. But to get this information, the test suite has to run for almost 95 minutes to complete all tests — and that’s a long waiting time! (The worst part was that sometimes the test failed at minute 90... and it was very frustrating.)

We wanted to test the suite to be scalable without sacrificing too much waiting time when running specs.

So this leads to a team discussion. We knew that we needed integration specs, there was no doubt about that. But over time our test suite will grow as the application and company do, so we wanted to test the suite to be scalable without sacrificing too much waiting time when running specs.

We decided to maintain a test suite that:

  • allowed us to read the specs easily,
  • would be easy for the non-technical team that was already used to reading Cucumber specs,
  • let the QA team create integration specs in the future by themselves,
  • works with a reliable tool that can be used in a production-level project, and avoids a super high learning curve for the new developers that come in the project.

Those were the initial requirements that lead us to use Gherkin, so the QA team would be able to read and create new integration specs much easier than using plain RSpec and Capybara.

Those requirements then lead us to Turnip.

Those requirements then lead us to Turnip, especially because it allows us to use Gherkin, can be integrated with the current RSpec setup, and especially because it allows loading the Rails server just once instead of twice (once for the unit test and the second for the Cucumber specs as Cucumber Rails do).

How did we use it?

The Turnip repo has a good README file, but we found a problem when loading the modules dynamically, because the README file is hardcoded into them and if you have multiple integration specs or modules to load, then it becomes a mess to try to manage all those hardcoded loaded modules. This is the way we implemented it. (If you have any improvement or more ideas I would love to read it in the comments.)

  1. Install Turnip
group :test do
  gem "turnip"
  1. Load Turnip RSpec into your .rspec file adding the following line:
-r turnip/rspec

Note: if you don’t have a .rspec, create it in your project root folder.

  1. Create a turnip_helper.rb file into you spec folder to load your module steps and make the configuration you need for your integration specs spec/turnip_helper.rb
require 'rails_helper'
require 'turnip/capybara'

def steps_module(file_path)
 modules_to_include = clean_step_modules(file_path)
 modules_to_include.map { |sub_module| sub_module.camelize }.join('::').constantize

def clean_step_modules(module_path)
 module_path.split('support/').last.gsub('.rb', '').split('/')

RSpec.configure do |config|
 Dir[Rails.root.join('spec/support/steps/**/*')].each do |f|
   config.include steps_module(f)
  1. Create your features into the features folder spec/features/your_features.feature
    Note that in our case we created a folder for each entity inside our application, for example: spec/features/profile/profile.feature , spec/features/user/user.feature

  2. Create your step modules, as you notice into the turnip_helper.rb the file we will automatically load all the modules that are located into the spec/support/steps/ folder, so you can create the folders you need to manage them. What I did was create a folder for each entity or related steps. For example, I made a spec/support/steps/commons/utils_steps.rb that defines all the steps that are shared across multiple pages but don’t belong to any specific entity, and spec/support/steps/profile/profile_steps.rb is the file I created to write all the steps related to the profile entity and its behavior. Nevertheless, you can use any structure you need and works for you.

  3. Run your specs bundle exec rspec spec/ or just your features bundle exec rspec spec/features

Final result

In the end, we should end up with something like the following:


Feature: When I visit my dashboard as a provider
    Given there is a valid-user
    And user is logged in

  Scenario: I should see my profile image and a greeting
    Then I should see 'Welcome, Elon' text
    And I should see the provider profile image


module Steps
  module Commons
    module InitialDataSteps
      step 'there is a valid user' do
        # Your logic to add users


module Steps
  module Login
    module LoginSteps
      step 'user is logged in' do
# Your logic to log in users


module Steps
  module Profile
    module ProviderProfileSteps
      step 'I should see :searched_text text' do |searched_text|
        expect(page).to have_content(searched_text)

      step 'I should see provider profile image' do
        expect(page).to have_selector('img.provider-image')

As you notice we have placed our steps in different files that are in different folders, that is because the configuration we have in the turnip helper will load all the modules located into the spec/support/steps folder and their children.

I hope this post has helped you to configure Turnip into your project, or at least it has convinced you to use Turnip — or at least give it a try.

As always, if you have any doubts I would like to read them in the comments.

Thanks for reading!

@OzmarUgarte, Senior Consultant at MagmaLabs

You May Also Like
Read More

Setting Up EasyPost on Solidus

Reading Time: 5 minutes Solidus provides a flexible system to calculate shipping by accommodating a wide range of shipment pricing: from simple flat…