How to setup a Rails app for Test-Driven- and Behavior-Driven Development with Rspec and Capybara
Continuing forward in our introduction to web and mobile development tutorial series, here we'll learn how to prep our Rails app to begin writing code in a Test-Driven Development (TDD) and Behavior-Driven Development (BDD) paradigm.
[Author's note: I wrote the first couple dozen tutorials in this series a few years ago and I'm in the process of updating the content to reflect the evolution of best practices in the industry. Please comment if you see anything I missed that should be updated. Thanks!]
GET A RAILS APP SETUP THE NON-TDD/BDD WAY
If you are just tuning in, you can either quickly scan the previous post to setup a Rails app, clone the app from Github and “git checkout tags/v0.1” to sync to the exact spot I'm writing from here, or you can use your own Rails 4+ app that hasn't yet been TDD/BDD-ified.
Writing Rails code the non-TDD/BDD way, FYI, means you simply plow forward setting up and filling in your models, controllers, views, routes, partials, helpers, JavaScripts, etc. without bothering to test anything in an automated way. You simple write code, refresh the browser, click some stuff maybe, and repeat.
This is all fine and dandy until you start working with other developers, deploy updates to production, and/or revise code you (or someone else) has written before. In other words, if you're attempting to build anything serious then you absolutely need to write code in a TDD/BDD way!!!!
WHAT IS TDD AND BDD?
In short, TDD is writing tests first, then writing code in your app to make the tests pass. This way you always have a safety net built and up-to-date so you can sleep better at night when you, or someone else, revises your code.
BDD is essentially the same as TDD, but it writes tests from a “user story” perspective, which helps solves a bunch of common software development problems. The wikipedia article on Behavior-Driven Development is actually quite good and you should give it a quick scan (or thorough read) if you want to dive into more details.
For our purposes here, I'll walk you through how to setup your Rails app for TDD/BDD using RSpec, Capybara, and a sweet Capybara driver calledCapybara-webkit. We'll use Rubygems called factory_girl_rails and faker to help setup our resources in our test environment (these safe us a ton of time, you'll see!).
GET YOUR GEMFILE PREPPED
Ok, first install the Qt libraries on your local machine (to ensure capybara-webkit will install correctly, then open up your Gemfile and add these guys in there:
. . .
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
end
group :test do
gem 'faker'
gem 'capybara-webkit'
end
. . .
Now run “bundle install” in your terminal (from your Rails app root directory) and make sure everything installs correctly. If you run into problems, copy the error message into Google and follow instructions you see from other developers who had the same issue(s).
GET YOUR TEST DATABASE PREPPED
Open up your database.yml file and make sure your test database configs are good to go. If you've been following along in this tutorial series, then you shouldn't need to change anything here:
<em>#config/database.yml </em>
. . .
test:
adapter: postgresql
encoding: unicode
database: OurAgendaApp_test
pool: 5
username: OurAgendaApp
password:
. . .
If you're just tuning in, you'll want to make sure your adapter, database name, and database user are synced up to your local settings.
From here you'll want to run these commands in your terminal:
$ rake db:create:all
$ rake db:migrate RAILS_ENV=test
Again, if you run into problems, double check your configuration settings and Google any errors you get to find a solution.
SETUP RSPEC, CAPYBARA-WEBKIT, AND FACTORY GIRL
Ok, now let's set things up. First, run this to let Rspec setup itself:
$ rails g rspec:install
…and let's open up that spec/rails_helper.rb file and add three important lines:
. . .
#add this line under the other ones like it up top
require "capybara/rspec"
#set the default driver
Capybara.javascript_driver = :webkit
. . .
RSpec.configure do |config|
. . .
#add this line at the bottom of the config section
#it saves us time when using FactoryGirl methods.
config.include FactoryGirl::Syntax::Methods
end
Cool. Now let's open up the .rspec file (in the root dir of our app) and add this line to the bottom:
--format documentation
Finally, let's open up our config/application.rb file and add:
module OurAgendaApp
class Application < Rails::Application
#add these lines
config.generators do |g|
g.stylesheets false
g.javascripts false
g.test_framework :rspec,
:fixtures => true,
:view_specs => false,
:helper_specs => false,
:routing_specs => false,
:controller_specs => true,
:request_specs => true
g.fixture_replacement :factory_girl, :dir => "spec/factories"
end
WRITE YOUR FIRST TESTS
In our example app for this tutorial series we'll start with the user model and write a “unit” test for it (i.e. a test for a small unit of our code, which in a Rails context is usually a model — sometimes you'll even here the phrase ‘model test'). If you're writing your own app and following along, you can go ahead and use your own user model or another core model.
Let's start by setting up our user factory. Create a folder within your spec/ folder called “factories” and create a file called “users.rb” in it:
#spec/factories/users.rb
require 'faker'
FactoryGirl.define do
factory :user do
email {Faker::Internet.email}
password '123'
password_confirmation '123'
end
end
And now let's create our user spec file. Create a another folder within your spec/ folder called “models” and create a file called “user_spec.rb” in it:
#spec/models/user_spec.rb
require 'rails_helper'
describe User do
it "has a valid factory" do
build(:user).should be_valid
end
it "is invalid without an email" do
build(:user, email: nil).should_not be_valid
end
end
FYI, as you add more controllers and models to your app, stub files for testing will be automatically created for you (this is what our configuration lines above took care of for us).
RUN YOUR FIRST TESTS
In your terminal, go ahead and run your tests:
$ rspec
You should see output indicating that your user unit has a valid factory but does NOT pass the “is invalid without an email” test. You'll get specific output about where your test failed, and what you need to do to fix it.
Ok cool, welcome to Test-Driven Development. Now let's go write code to make sure our user model “is invalid without an email.” How do we do that? Add a validation in our model file, of course!
#app/models/user.rb
class User < ActiveRecord::Base
. . .
validates :email, presence: true
. . .
end
Now run “rspec” in your terminal again and you should see both tests pass.
Congrats! Now before you write any more code in your app, it's time to go write a bunch of tests.
HOMEWORK TIME
For those following along in our tutorial series, now is the time to fill out the unit tests for our Action Items, Agenda Items, Meetings, and Comments. I'll let you take it from here. You can read more about the Domain Specific Language (DSL) for Capybara here to learn about all the cool things you can do with it to test your app. For those following along with their own apps, you should fill out your unit tests now. Don't go too crazy, some tests like it “is invalid without a first name” and such may not be necessary to write (i.e. if they are super obvious).
In future posts in this series, we'll write “feature” tests (which are staples of BDD. That is, end-to-end tests that are related to ‘integration', ‘functional', and ‘acceptance' tests. This post does a great job explaining the nuances and relationships between them.)
- — -
In the next post in this series we will begin the process of merging in cutups (i.e. HTML, CSS, and JavaScript files). Previous post: How to design and prep a Ruby on Rails model architecture.