Below is how I set up a fresh Rails application for testing. I'll describe it in three parts:
- An application template that can add all the necessary gems and configuration
- My setup process (commands I run to create a new Rails app)
- A breakdown of the gems I use
Let's start with the application template.
My application template
First, if you don't know, it's possible to create a file called an application template that you can use to create a Rails application with certain code or configuration included. This is useful if you create a lot of new Rails applications with parts in common.
Here's an application template I created that will do two things: 1) install a handful of testing-related gems and 2) add a config file that will tell RSpec not to generate certain types of files. A more detailed explanation can be found below the code.
gem_group :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' gem 'capybara' gem 'webdrivers' gem 'faker' end initializer 'generators.rb', <<-CODE Rails.application.config.generators do |g| g.test_framework :rspec, fixtures: false, view_specs: false, helper_specs: false, routing_specs: false, request_specs: false, controller_specs: false end CODE after_bundle do generate 'rspec:install' end
The first chunk of code will add a certain set of gems to my Gemfile. A more detailed explanation of these gems is below.
The second chunk of code creates a file at config/initializers/generators.rb. The code in the file says "when a scaffold is generated, don't generate files for fixtures, view specs, helper specs, routing specs, request specs or controller specs". There are certain kinds of tests I tend not to write and I don't want to clutter up my codebase with a bunch of empty files. That's not to say I never write any of these types of tests, just sufficiently rarely that it makes more sense for me to create files manually in those cases than for me to allow files to get generated every single time I generate a scaffold.
The setup process
When I run rails new, I always use the -T flag for "skip test files" because I always use RSpec instead of the Minitest that Rails comes with by default.
Also, incidentally, I always use PostgreSQL. This choice of course has little to do with testing but I'm including it for completeness.
In this particular case I'm also using the -m flag so I can pass in my application template. Application templates can be specified using either a local file path or a URL. In this case I'm using a URL so that you can just copy and paste my full rails new command as-is if you want to.
$ rails new my_project -T -d postgresql \ -m https://raw.githubusercontent.com/jasonswett/testing_application_template/master/application_template.rb
Once I've created my project, I add it to version control. (I could have configured my application template to do this step manually, but I wanted to explicitly show it as a separate step, partially to keep the application template clean and easily understandable.)
$ git add . $ git commit -a -m'Initial commit'
The gems
Here's an explanation of each gem I chose to add to my project.
rspec-rails
RSpec is one of the two most popular test frameworks for Rails, the other being Minitest.
The rspec-rails gem is the version of the RSpec gem that's specifically fitted to Rails.
factory_bot_rails
Factory Bot is a tool for generating test data. Most Rails projects that use RSpec also use Factory Bot.
Like rspec-rails, factory_bot_rails is a Rails-specific version of a more general gem, factory_bot.
capybara
Capybara is a tool for writing acceptance tests, i.e. tests that interact with the browser and simulate clicks and keystrokes.
The underlying tool that allows us to simulate user input in the browser is called Selenium. Capybara allows us to control Selenium using Ruby.
webdrivers
In order for Selenium to work with a browser, Selenium needs drivers. There are drivers for Chrome, drivers for Edge, etc. Unfortunately it can be somewhat tedious to keep the drivers up to date. The webdrivers gem helps with this.
faker
By default, Factory Bot (the tool for generating test data) will give us factories that look something like this:
FactoryBot.define do factory :customer do first_name { "MyString" } last_name { "MyString" } email { "MyString" } end end
This is fine for just one record but becomes a problem if we have multiple records plus a unique constraint. If in this example we require each customer to have a unique email address, then we'll get a database error when we create two customer records because the email address of MyString will be a duplicate.
One possible solution to this problem is to replace the instances of "MyString" with something like SecureRandom.hex. I don't like this, though, because I often find it helpful if my test values resemble the kinds of values they're standing in for. With Faker, I can do something like this:
FactoryBot.define do factory :customer do first_name { Faker::Name.first_name } last_name { Faker::Name.last_name } email { Faker::Internet.email } end end
This can make test problems easier to troubleshoot than when test values are simply random strings like c1f83cef2d1f74f77b88c9740cfb3c1e.
Honorable mention
I also often end up adding the VCR and WebMock gems when I need to test functionality that makes external network requests. But in general I don't believe in adding code or libraries speculatively. I only add something once I'm sure I need it. So I typically don't include VCR or WebMock in a project from the very beginning.
Next steps
After I initialize my Rails app, I usually create a walking skeleton by deploying my application to a production and staging environment and adding one small feature, for example the ability to sign in. Building the sign-in feature will prompt me to write my first tests. By working in this way I front-load all the difficult and mysterious work of the project's early life so that from that point on, my development work is mostly just incremental.