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 compatability 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:

$ 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:

$ 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 'joe.user@example.com'
  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

Rails: Configuring RSpec for Spork

RSpec is great for behaviour driven development in Rails. However, as the amount of code you test increases, so too does the time taken to run the tests.

To help speed things up, I’ve been using Spork; a small server that preloads all your library code and sits waiting for specs to run. Installing and setting up your project to use Spork is quick to do:

# Spork relies on cucumber
$ gem install cucumber spork
 
# Prepares your project for Spork
$ cd railsap
$ spork --bootstrap

Once your project is bootstrapped, you’ll need to move all your RSpec initialisation code into the prefork block that Spork has added to your spec_helper.rb:

# spec/spec_helper.rb
require 'rubygems'
require 'spork'
 
Spork.prefork do
  # All the default RSpec stuff goes here, e.g:
  ENV["RAILS_ENV"] ||= 'test'
  require File.dirname(__FILE__) + "/../config/environment"
  require 'spec/autorun'
  require 'spec/rails'
  Spec::Runner.configure do |config|
    # ...
  end
end
#...

Now you can run a Spork server instance with:

$ spork

For me, though, Spork refused to run. It just kept throwing the following exception:

Preloading Rails environment
Loading Spork.prefork block...
uninitialized constant MissingSourceFile (NameError)

Fixing the configuration for Spork

For a short while, I skipped Spork and continued running my specs as normal. Eventually the number of specs I was testing took several seconds for each run, which soon added up to a lot of waiting.

So, after some searching and thanks to this post on Google Groups, I learned the exception is caused by a condition included as part of the default RSpec initialisation code.

The solution is to remove the condition from your spec_helper.rb:

# spec/spec_helper.rb
# ...
# before:
# require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
require File.dirname(__FILE__) + "/../config/environment"

Now you should be able to run Spork with:

$ spork
Using RSpec
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!

Finally, you can tell RSpec to connect to and use Spork if it is running. Just add –drb to your spec.opts file:

# spec/spec.opts
--colour
--format progress
--loadby mtime
--reverse
--drb

Note that these options are only used if you run RSpec through the rake spec: tasks. If you run specs manually, remember to add the –drb option, for example:

$ script/spec --colour --drb spec/controllers/users_controller_spec.rb

Changing your spec setup code by removing the condition is obviously far from ideal, and it may break compatability with cucumber (see this lighthouse ticket for more information). However, it at least means you can take advantage of Spork until a proper fix is implemented.