Rails: Setting Up for Testing
As I started work on a new Rails project recently, one of my first priorities was to set up my test environment and tools so I could use test driven development (TDD) from the beginning. Here are the steps I took, some snags I ran into, and some things I learned along the way.
My first step was to modify my Gemfile. I replaced 'sqlite3' with 'pg' for my database. I'll be deploying to Heroku, and I want my development and test environment to be as close to production as possible. (I added 'rails_12factor' and 'unicorn' while I was at it. I'll need them for deployment anyway...) I also added 'figaro' to manage my environment variables.
Specifically for testing, I added:
group :development, :test do
gem 'factory_girl_rails'
gem 'rspec-rails', '~< 3.0.0.beta'
gem 'capybara', '~< 2.4.3' # To simulate user interaction with browser
gem 'capybara-webkit' # For pages with JavaScript - so they load in real webpage
gem 'database_cleaner' # To clean database after capybara-webkit messes it up
gem 'debugger'
gem 'launchy'
end
After running bundle install
and rails g rspec:install,
I noticed something interesting. My spec folder had the spec_helper.rb file I expected, but it also had a new file I'd never seen before - rails_helper.rb. Some Googling revealed that rails_helper.rb is basically what spec_helper.rb used to be, and spec_helper.rb has been stripped down. The docs explained that spec_helper.rb now matches the file that's generated by Rspec when it's used on it's own (without Rails), and it provides a way to run tests that don't require Rails without loading Rails up. Cool.
The only change I made to spec_helper.rb was to uncomment the line about running specs in random order, which magically uncommented the other defaults as well. The defaults were what I wanted, so I left them alone. I had a little more work to do in rails_helper.rb though. With my additions, the top of the file now looks like this:
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'database_cleaner'
I remembered from a past project that I needed a different driver for tests with JavaScript, and I needed to configure database_cleaner differently depending on the type of test, too. I couldn't remember exactly how to do it, though. At first I copied and pasted the code from my older project, but there were a couple of things that looked fishy, so I started reading up on those topics again. I was glad I did, because my old project had some redundancies and things that just didn't make sense upon closer inspection. For the new project, I set up capybara-webkit as the driver for JS tests and configured database-cleaner to use a truncation strategy for tests with JavaScript and transaction for everything else. This is what I ended up addinginside the Rspec.configure block:
RSpec.configure do |config| # Use capybara-webkit for tests with JavaScript
Capybara.javascript_driver = :webkit
# Setup for database cleaning between tests
# Different strategy needed for testing with JavaScript
config.before(:each) do |example|
DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
endend
I edited config/database.yml to use postgres instead of sqlite. Otherwise, I left the defaults in place, including the database names. That turned out to be a mistake, though I didn't realize it until later. After I created my first models - including a User model - and ran rake db:migrate,
I got an error that the User table already existed. I'd forgotten that postgres databases aren't contained within individual Rails projects. All of my projects use Postgres.app as the common server. I used the default database names on another project, so now my new project thought it was supposed to use the same database and threw an error when I tried to create a new User table in a database that already had one. I changed the database names for development and testing, but I left the production settings as they were. I probably should have changed them too just to be thorough, but I know Heroku overwrites that file with its own settings on deployment. This is what my database.yml file looked like after I made all the changes:
production:
adapter: postgresql
encoding: unicode
database: db/production
pool: 5
timeout: 5000
development:
adapter: postgresql
encoding: unicode
database: db/preschool_development
pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: postgresql
encoding: unicode
database: db/preschool_test
pool: 5
timeout: 5000
After I created and migrated the database successfully, I thought I was ready to go. I wrote a simple spec and tried to run it in the Terminal just to confirm that everything was working, but my Terminal screen blew up with the longest error message I'd ever seen. Something about circular loading of spec_helper.rb... Much searching later, I learned that the problem was in the .rspec file. I needed to delete the line there requiring spec_helper. I took out the warnings line, too. That could be dangerous, but Stack Overflow posts suggested it, and an open source project I've worked on had it taken out as well. I have a lot of faith in the person who set up that project, so I figured it would be safe to follow suit.
After taking out those lines, my spec ran as expected. I was finally ready for some TDD.