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.