Fixtures aren't fun. Machinist is.
Switch branches/tags
Nothing to show
Pull request Compare This branch is 2 commits ahead, 201 commits behind notahat:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Fixtures aren't fun. Machinist is.

Machinist lets you construct test data on the fly, but instead of doing this:

describe Comment do
  before do
    @user = User.create!(:name => "Test User")
    @post = Post.create!(:title => "Test Post", :author => @user, :body => "Lorem ipsum...")
    @comment = Comment.create!(
      :post => @post, :author_name => "Test Commenter", :author_email => "",
      :spam => true

  it "should not include comments marked as spam in the without_spam named scope" do
    Comment.without_spam.should_not include(@comment)

you can just do this:

describe Comment do
  before do
    @comment = Comment.make(:spam => true)

  it "should not include comments marked as spam in the without_spam named scope" do
    Comment.without_spam.should_not include(@comment)

Machinist generates data for the fields you don't care about, and constructs any necessary associated objects.

You tell Machinist how to do this with blueprints:

require 'faker'  { } { }
Sham.title { Faker::Lorem.sentence }
Sham.body  { Faker::Lorem.paragraph }

User.blueprint do
  name { }

Post.blueprint do
  title  { Sham.title }
  author { User.make }
  body   { Sham.body }

Comment.blueprint do
  author_name  { }
  author_email { }
  body         { Sham.body }


Install the plugin:

./script/plugin install git://

Create a blueprints.rb in your test (or spec) directory, and require it in your test_helper.rb (or spec_helper.rb):

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

Set Sham to reset before each test. In the class Test::Unit::TestCase block in your test_helper.rb, add:

setup { Sham.reset }

or, if you're on RSpec, in the Spec::Runner.configure block in your spec_helper.rb, add:

config.before(:each) { Sham.reset }

Installing as a Gem

If you'd prefer, you can install Machinist as a gem:

gem sources -a
sudo gem install notahat-machinist

From there, create the blueprints.rb file as described above, and make sure you require machinist and sham.

Sham - Generating Attribute Values

Sham lets you generate random but repeatable unique attributes values.

For example, you could define a way to generate random names as: { (1..10).map { ('a'..'z').to_a.rand } }

Then, to generate a name, call:

So why not just define a method? Sham ensures two things for you:

  1. You get the same sequence of values each time your test is run
  2. You don't get any duplicate values

Sham works very well with the excellent Faker gem by Benjamin Curtis. Using this, a much nicer way to generate names is: { }

Sham also supports generating numbered sequences if you prefer. {|index| "Name #{index}" }

If you want to allow duplicate values for a sham, you can pass the :unique option:

Sham.coin_toss(:unique => false) { rand(2) == 0 ? 'heads' : 'tails' }

You can define a bunch of sham methods in one hit like this:

Sham.define do
  name          { }
  email_address { }

Blueprints - Generating ActiveRecord Objects

A blueprint describes how to build a generic object for an ActiveRecord model. The idea is that you let the blueprint take care of constructing all the objects and attributes that you don't care about in your test, leaving you to focus on the just the things that you're testing.

A simple blueprint might look like this:

Comment.blueprint do
  body "A comment!"

Once that's defined, you can construct a comment from this blueprint with:


Machinist calls save! on your ActiveRecord model to create the comment, so it will throw an exception if the blueprint doesn't pass your validations. It also calls reload after the save!.

You can override values defined in the blueprint by passing parameters to make:

Comment.make(:body => "A different comment!")

Rather than providing a constant value for an attribute, you can use Sham to generate a value for each new object:

Sham.body { Faker::Lorem.paragraph }
Comment.blueprint do
  body { Sham.body }

Notice the curly braces around Sham.body. If you call Comment.make with your own body attribute, this block will not be executed.

You can use this same syntax to generate associated objects:

Comment.blueprint do
  post { Post.make }

If you're assigning an associated object this way, Machinist is smart enough to look at the association and work out what sort of object it needs to create, so you can just write:

Comment.blueprint do

You can refer to already assigned attributes when constructing a new attribute:

Comment.blueprint do
  body { "Comment on " + }

You can also override associated objects when calling make:

post = Post.make
3.times { Comment.make(:post => post) }

It's common to need to construct an object with particular attributes, or a particular object graph, in a number of tests. The best way to abstract out the construction is to put something like this in your blueprints.rb:

class Post
  def self.make_with_comments(attributes = {})
    Post.make(attributes) do |post|
      3.times { Comment.make(:post => post) }

Note that make can take a block, into which it will pass the newly constructed object.

If you want to generate an object graph without saving to the database, use make_unsaved:


This will generate both the Comment and the associated Post without saving either.

Using Blueprints in Rails Controller Tests

The plan method behaves like make, except it returns a hash of attributes, rather than saving the object. This is useful for passing in to controller tests:

test "should create post" do
  assert_difference('Post.count') do
    post :create, :post => Post.plan
  assert_redirected_to post_path(assigns(:post))

You an also call plan on ActiveRecord associations, making it easy to test nested controllers:

test "should create comment" do
  post = Post.make
  assert_difference('Comment.count') do
    post :create, :post_id =>, :comment => post.comments.plan
  assert_redirected_to post_comment_path(post, assigns(:comment))

Blueprints - Gotchas

Some ActiveRecord objects have attributes that don't play nicely with machinist.

For example:

OpeningHours.blueprint do
  open { }

This will result in Machinist attempting to run ruby's open command. To work around this use instead.

OpeningHours.blueprint do { }


Written by Pete Yandell.


Thanks to Thoughtbot's Factory Girl. Machinist was written because I loved the idea behind Factory Girl, but I thought the philosophy wasn't quite right, and I hated the syntax.

Copyright (c) 2008 Peter Yandell, released under the MIT license