Building apps with Rails 3, RSpec, FactoryGirl and Mocha.

Clone the source code from github.

It's no secret that Rails 3 has been released in beta for a little while now. Like most Rails developers, I was keen to try out some of the new functionality, not least the redesigned Active Record API.

Some heavy duty googling turned up little information on how to get an app started and configured with all the associated goodies we've come to expect: rspec, mocha and factory_girl.

This is probably because most of these plugins are in a state of development to bring full compatibility with Rails 3. So until that happens, I thought I'd document how I've got a skeleton app up and running on Ubuntu using the current development branches.

You can find the latest versions of the code behind on my github. I'll continue to revise this template as I figure out more about Rails 3, and the plugins.

Installing Rails 3

There's plenty of posts detailing how to get Rails 3 on your machine. Favouring gems, I followed the official advice:

$ gem install tzinfo builder memcache-client rack rack-test rack-mount

$ gem install erubis mail text-format thor bundler i18n

$ gem install rails --pre

$ rails -v

Rails 3.0.0.beta

With everything set, we can spin a new app using:

bash$ rails myapp

# Lots of output

$ cd myapp

Bundling Gems

Rails 3 uses the Bundler gem to configure its environment. Bundler brings a lot of benefits, including dependency resolution and ensures that an app's gem requirements are met.

Your app's required gems are declared in a Gemfile in your project root. A default Gemfile is provided to load Rails 3 and SQLite. For our project, we'll update it to require some additional gems:

# Gemfile

source 'http://gemcutter.org'

gem "rails", "3.0.0.beta"

gem "sqlite3-ruby", :require => "sqlite3"

# Require the haml gem for rendering HAML templates and SASS stylesheets

gem "haml"

# FactoryGirl and Shoulda Rails 3 development branches from github

group :thoughtbot do

  # I've included shoulda here in case you use TestUnit. Shoulda macros for RSpec 2 don't

  # work yet though.

  gem "shoulda", :git => "git://github.com/sinefunc/shoulda.git",

                 :branch => "rails3"

  gem 'factory_girl', :git => 'git://github.com/thoughtbot/factory_girl.git',

                      :branch => 'rails3',

                      :require => "factory_girl"

end

group :test do

  # Install development release of rspec (includes rspec-rails)

  gem "rspec-rails", ">= 2.0.0.a9"

  gem "webrat"

  gem "mocha"

end

Now you can install the bundled gems with:

$ bundle install

Configuring RSpec and FactoryGirl

One of the first tools I felt lost without in Rails 3 was RSpec. Although I tried going back TestUnit, I missed the clarity of RSpec. With RSpec 2 installed by Bundler, the app can be prepared for specs with:

$ rails generate rspec:install

This creates the appropriate spec folder and configuration files. I configured RSpec to use FactoryGirl and Mocha. Notice that in the Gemfile above, Shoulda and FactoryGirl are declared in a group :thoughtbot. Bundler loads the default gems before Rails has initialised environment constants such as Rails.root. By declaring these gems within a group, they can be defered until Rails has setup the environment (Reference).

For RSpec to use FactoryGirl, we ask Bundler to load the :thoughtbot gems in spec_helper.rb:

# specs/spec_helper.rb

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

require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)

require 'rspec/rails'

# Load in RSpec and Shoulda

# Shoulda macros don't seem to work with RSpec 2. When they do, this is where to require them.

# require 'shoulda'

# require 'shoulda/rspec'

require 'factory_girl'

# Autoloading of Factories seems to be broken, so manually require the <tt>factories.rb</tt> file

require File.dirname(__FILE__) + "/../spec/factories.rb"

# Requires supporting files with custom matchers and macros, etc,

# in ./support/ and its subdirectories.

Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}

For now I've left shoulda out of the configuration as the Shoulda macros don't yet work with RSpec 2. I also manually required spec/factories.rb as FactoryGirl's autoloader also seemed not to be working yet. Before running a spec, you'll need to declare your factories in spec/factories.rb:

bash$ touch spec/factories.rb

Finally, switch RSpec to use mocha for mocking objects:

# spec/spec_helper.rb

# == Mock Framework

config.mock_with :mocha

Writing specs

With everything setup, it's time to write the first spec. As a simple example, I'll create a User model and its associated spec. With RSpec installed, the standard rails model generator will automatically create an associated spec file:

$ rails generate model user email_address:string password_hash:string password_salt:string
$ rake db:migrate
==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0041s
==  CreateUsers: migrated (0.0043s) ===========================================

The spec for the User model can now be written using the familiar RSpec dialect:

# spec/models/user_spec.rb
require 'spec_helper'

describe User do
  it "should be valid" do
    @user = Factory.create(:user)
    @user.should be_valid
  end
end

I've also used Factory.create to build a user, so as before, you'll need to declare your user factory in spec/factories.rb:

# spec/factories.rb

Factory.define :user do |u|
  u.email_address '[email protected]'
  u.password 'secret'
end

Finally, to run your specs, you use rake:

$ rake spec

All being well, RSpec should now load up and run your specs. In this example, the spec fails trying to set User#password. To fix this, we'll add a virtual attribute, password, to the User model that sets a salted, hashed password:

# app/models/user.rb

require 'digest/sha2'

class User < ActiveRecord::Base

  def password=(pass)
    @password =  pass
    return if @password.blank?

    self.password_salt = Digest::SHA2.hexdigest(Time.now.to_s + rand(32000).to_s)

    self.password_hash = Digest::SHA2.hexdigest(@password + self.password_salt)
  end

protected

  def password
    @password
  end

end

With that, another call to rake should report your passing spec:

$ rake spec
.
Finished in 0.062692 seconds
1 example, 0 failures

With this, you can now start to build your Rails 3 app, complete with specs and factories. I'll continue to update the code on github as I figure out more, and as more plugins become compatible with Rails 3.

References