Factory pattern with syntactic sugar

Dan Manges has a nice write-up on why the
factory pattern is better than Rails fixtures: Rails: Fixin’ Fixtures
with Factory
. The factory is used to
create valid objects for testing with default values for all of the
fields. These objects can be used in tests without cluttering the test
with attributes we do not care about.

On my current project, we like the factory pattern but favor a slightly
better syntax. We wanted to replace:

Factory.create_paperboy

with

Paperboy.build!

We use the build method (for lack of a better name) to create test data.
We are calling a class method on Paperboy in order to create an
instance, which seems more consistent with object creation in ruby than
calling a method on a Factory class:

Paperboy.new
Paperboy.create
Paperboy.build!

In addition to build! (which is like create!), we added a build method
which creates the object without saving it. We put our code in a file
called factory.rb (which we require in spec_helper.rb) that looks like:

module Factory

  def self.included(base)
    base.extend(self)
  end

  def build(params = {})
    raise "There are no default params for #{self.name}" unless self.respond_to?(self.name.underscore)
    new(self.send(self.name.underscore).merge(params))
  end

  def build!(params = {})
    obj = build(params)
    obj.save!
    obj
  end

  def customer
    {
      :first_name => "Joe",
      :last_name  => "Guy",
      :paperboy   => Paperboy.build,
    }
  end

  def newspaper
    {
      :customer => Customer.build,
      :headline => "Read all about it!",
      :paperboy => Paperboy.build,
    }
  end

  def paperboy
    {
      :first_name     => "Paper",
      :last_name      => "Boy",
      :delivery_route => "Main St Route"
    }
  end
end

ActiveRecord::Base.class_eval do
  include Factory
end

The build method uses the class name to find the default params, which
are defined as a method per class. Then, it merges any user supplied
params and creates the object. Now, when we see test code that looks
like:

Paperboy.new :first_name => "some", :last_name => "person"

we can replace it with:

Paperboy.build

or, if one of the fields is important for the test:

Paperboy.build :first_name => "joe"

It is easy to swap the word “new” or “create” for “build” or “build!”
and then delete the params that we do not care about.

Paul Gross

Paul Gross

I'm a lead software developer in Seattle working for Braintree Payments.

Read More