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_paperboywith
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.buildor, 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.
5 Responses to “Factory pattern with syntactic sugar”
Sorry, the comment form is closed at this time.
Hi Paul,
Where do you “require” the file from? Would you put the file in the “lib” directory and then “require” it from app/controllers/application_controller.rb?
Or should it be “required” only when in the test environment? In which case which approach would you recommend to require this?
Cheers
Hi Paul,
It feels like to me, this is a combination between the builder and the factory pattern, something that I’ve been favouring more so on my projects. It looks a lot more elegant in Ruby of course!
Cheers.
Hey Pat,
We actually started with something that was more like builder, but the complexity was getting in the way, so we simplified to the solution above.
Thanks for this, it is a good way to handle default values. I found one problem, in that if you have a class called (say) ‘Name’ you can get a conflict in your name spaces and some weird errors come up when you inadvertently overwrite various methods in ActiveRecord::Base because of the class_eval include at the bottom of the factory.rb file.
Fixing it was simple, just prefix the def paperboy, customer etc methods with some arbitrary thing like’_factory_’ and then change the build method like this:
unless self.respond_to?(“_factory_#{self.name.underscore}”)
and
new(self.send(“_factory_#{self.name.underscore}”).merge(params))
Don’t know how all that will come out in Textile, but we’ll see
This way you don’t get any class method clashes.
Mikel
Hi Greg, we put this file into the test (or spec) folder and then require it from test_helper (or spec_helper). This makes it available for tests and nothing else.