Machinist

Fixtures aren't fun. Machinist is.

Machinist makes it easy to create test data within your tests. It generates data for the fields you don't care about, and constructs any necessary associated objects, leaving you to only specify the fields you do care about in your tests. For example:

describe Comment do
before do
# This will make a Comment, a Post, and a User (the author of
# the Post), and generate values for all their attributes:
@comment = Comment.make(:spam => true)
end
it "should not include comments marked as spam in the without_spam named scope" do
Comment.without_spam.should_not include(@comment)
end
end

Blueprints - Generating Objects

A blueprint describes how to generate an object. The idea is that you let the blueprint take care of making up values for attributes that you don't care about in your test, leaving you to focus on the just the things that you're testing.

When you call make, Machinist calls Post.new, then runs through the attributes in your blueprint, calling the block for each attribute to generate a value. The Post is then saved and reloaded. An exception is thrown if Post can't be saved.

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

Post.make(:title => "A Specific Title")

If you don't supply a block for an attribute in the blueprint, Machinist will look for a Sham definition with the same name as the attribute, so you can shorten the above blueprint to:

Post.blueprint do
title
author { Sham.name }
body
end

If you want to generate an object without saving it to the database, replace make with make_unsaved. (make_unsaved also ensures that any associated objects that need to be generated are not saved - although not if you are using Sequel. See the section on associations below.)

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

Machinist will not call the blueprint block for the post attribute, so this won't generate two posts.

Machinist is smart enough to look at the association and work out what sort of object it needs to create, so you can shorten the above blueprint to:

Comment.blueprint do
post
end

Other Associations

For has_many and has_and_belongs_to_many associations, ActiveRecord insists that the object be saved before any associated objects can be saved. That means you can't generate the associated objects from within the blueprint.

plan will save any associated objects. In this example, it will create an Author, and it knows that the controller expects an author_id attribute, rather than an author attribute, and makes this translation for you.

You can also call plan on has_many associations, making it easy to test nested controllers: