Written by: steve ross on April 2nd 2010

In my last post, I referred to my talk and the presentation materials. One of the things I did was cast fixture replacement in terms of Fixjour. An alternate, and equally capable (IMO) fixture replacement tool exists: machinist.

Why fixture replacement? Because test fixtures are brittle. They break when your database schema changes. They break when you try to associate collections. They break on general principles. Mocks and stubs are cool but very tedious to create for each case you want to test. Mocks and stubs are custom made on an as-needed basis for isolating the unit under test from outside dependencies. However, they too can become brittle and it can take quite a while to debug the problem. Fixture replacement is a wonderful alternative that allows your test data generation to create objects with sensible defaults, and override them when necessary. Did I mention I'm sold on fixture replacement.

So, in the presentation, I talk about Fixjour, which is very lean and does manufacture objects quite nicely. However, Machinist has proven itself for me in a number of larger projects, so it's worth explaining how to use it. My advice, try them both on a small project and see which feels more natural. That's the right one.

Here's how to retrofit the presentation project with machinist.

sudo gem install notahat-machinist --source http://gemcutter.org

In test.helper, change the config.gem from fixjour to:

config.gem 'notahat-machinist', :lib => 'machinist'@

Do it exactly as shown, as the require will be expecting a lib called machinist, but the gem is called notahat-machinist. Just saving you steps...

Create a features/support/blueprints.rb (Actually, where you put this is immaterial; you simply need to be able to access it from your specs and your features. Because Cucumber loads pretty much everything in the features directory and below, putting it in features/ makes for one less configuration issue).

require 'machinist/active_record'
require 'sham'

Sham.post_title { Faker::Lorem.sentence }
Sham.post_body  { Faker::Lorem.paragraph }
Sham.comment_body { Faker::Lorem.sentence }

Post.blueprint do
  title { Sham.post_title }
  body  { Sham.post_body  }

Comment.blueprint do
  body  { Sham.comment_body }
  pending_answer  7
  supplied_answer 7
  post  { Post.make }

def make_post_with_comments(attributes = {})
  post = Post.make(attributes)
  3.times { post.comments.make }

In config/environments/test.rb and config/environments/cucumber.rb, remove any trace of fixjour and add:

config.gem 'faker'  # If not already present!
config.gem 'notahat-machinist', :lib => 'machinist'

In spec/spec_helper.rb, remove all traces of fixjour and add This require:

require File.expand_path(File.dirname(__FILE__) + "/../features/support/blueprints")

Inside the config block, add the following two lines of code to ensure repeatable data for your tests:

config.before(:all)    { Sham.reset(:before_all)  }
config.before(:each)   { Sham.reset(:before_each) }

Now, look for all occurrences of create_post and create_comment in the project. Where you see create_post, use Post.make and where you see create_comment, use Comment.make. If you followed all the way through the presentation material to the point of having fixed the view helper, then change the make_post to make_post_with_comments. That way, you have a post with some comments all set up!