Development

Rails + RSpec + Turnip = <3


Reading Time: 4 minutes

Integration tests are used to ensure that all software components work together. In the RoR world, Cucumber is a very popular choice, mainly because it allows the use of Gherkins. This set of grammar rules lets us create tests that can be read in a more natural language, which even non-techy people can read and write — that makes all the business logic easier to understand. Turnip gives us all the advantages of Cucumber, but it’s been designed to be high performing in comparison with Cucumber.

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"
end
  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
end

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

RSpec.configure do |config|
 Dir[Rails.root.join('spec/support/steps/**/*')].each do |f|
   config.include steps_module(f)
 end
end
  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:

spec/features/profile/provider_provide.feature

Feature: When I visit my dashboard as a provider
  Background:
    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

pcpp_marketplace/spec/support/steps/commons/initial_data_steps.rb

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

pcpp_marketplace/spec/support/steps/login/login_steps.rb

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

pcpp_marketplace/spec/support/steps/profile/provider_profile_steps.rb

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

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

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

Rails
Capybara/Webkit: Running Integration Test
Quality Assurance
5 Steps to Become an Expert QA
Ecommerce
Payments Received With Stripe
Copy link
Powered by Social Snap