The purpose of this article is a step-by-step guide to get up and running with my preferred rails testing workflow (against rails 5.0.1/rspec 5.3). It could most likely become an application template as it’s mostly just copying and pasting.

This mostly covers testing, but other things will doubtless slip through.

Getting Started

rails new $app_path --database mysql --skip-bundle --skip-test
cd $app_path

At this point, I tend to tidy up the Gemfile somewhat and remove any Gems I’m not going to use. Then replace the groups at the end with something like this:

group :development do
  gem 'spring'
  gem 'spring-commands-rspec'

  gem 'capistrano-rails'

  gem 'guard'
  gem 'guard-rails'
  gem 'guard-rspec'

  gem 'rubocop', require: false
  gem 'reek', require: false
end

group :development, :test do
  gem 'byebug', platform: :mri

  gem 'rspec-rails', '~> 3.5'
  gem 'rspec-mocks'
  gem 'capybara'
  gem 'poltergeist'

  gem 'forgery'
  gem 'factory_girl_rails'
  gem 'seedbank'
end

group :test do
  gem 'database_cleaner'
  gem 'simplecov', require: false
end

Let’s install everything:

bundle install
rails generate rspec:install
mkdir spec/support

Now we need to tell rspec about the various other tools we’ll be using during test runs.

First, we’ll add the following to the top of spec/rails_helper.rb:

ENV['RAILS_ENV'] ||= 'test'

if ENV['RAILS_ENV'] == 'test'
  require 'simplecov'
  SimpleCov.start 'rails'
end

And the following line after the require 'rspec/rails' line:

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

Each of the following files will be loaded by the above, and should be placed in the newly created spec/support - with contents roughly as follows:

# spec/support/capybara.rb
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara/poltergeist'

Capybara.javascript_driver = :poltergeist
# spec/support/factory_girl.rb
RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods
end
# spec/support/database_cleaner.rb
RSpec.configure do |config|
  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    driver_shares_db_connection_with_specs = (Capybara.current_driver == :rack_test)
    DatabaseCleaner.strategy = driver_shares_db_connection_with_specs ? :transaction : :truncation
    DatabaseCleaner.start
  end

  config.append_after(:each) do
    DatabaseCleaner.clean
  end
end

Now we have a framework for testing which covers nearly every case. Have a look at the rspec subcommands under rails generate for the types of tests that can be generated.


The Components

rspec

A testing framework primarily targeted at BDD with a nifty DSL that lets you write expressive tests.

More than a test runner, rspec does all the heavy lifting to make writing and reading tests easy and fun.

capybara

An acceptance test framework for writing tests which simulate user behaviour. It uses a driver to run tests in a browser.

When combined with rspec, we can write a test like this:

RSpec.feature "Widget management", :type => :feature do
  scenario "User creates a new widget" do
    visit "/widgets/new"

    fill_in "Name", :with => "My Widget"
    click_button "Create Widget"

    expect(page).to have_text("Widget was successfully created.")
  end
end

Here, rspec provides the feature, scenario, and expect methods, while capybara provides the visit, fill_in, click_button and have_text methods.

forgery

A library for generating fake data for use in your tests. This is better than hard-coding test data, as it reduces the possibility of accidentally hard-coding your application to make the test pass.

For example:

Forgery(:internet).email_address
  #=> "krainboltgreene@crt.net"

See also faker.

factory_girl

A library for setting up test data objects.

Instead of manually creating objects in the database to use during a test run, factory_girl can create them for you (and has various options for persisting them or not).

Note: this is not fast, and should probably be avoided in unit tests in favour of unpersisted ActiveRecord objects, or using rspec mocks.

database_cleaner

Sometimes persisting to the database during a test is essential (feature tests) or desirable (you want to test some complex relationship), so ensuring each test begins with a clean slate is vital.

database_cleaner does this using a number of strategies (which we have configured above).

seedbank

Database seeds. Rails does have this built in, but seedbank gives us a little more structure, and some nice features like dependencies.

To Do

  • .editorconfig
  • .gitignore
  • linters config
  • ci config
  • responders