February 12, 2011

Associated Records with factory_girl

factory_girl is a Ruby Gem which makes it easy to build re-usable data objects in a test suite. It works especially well with ActiveRecord models.

I was recently working on adding a new feature to a project which contained some factory implementations written by a colleague, but the Cucumber steps kept failing where it was checking for associated records. This was because the associated records were set up incorrectly in the factory.

Below is an example of how we build associated records the right way. First is the parent model:

Factory.define(:booking) do |f|
  f.budget 12.40
  f.date 2.weeks.from_now
  f.number 23
  
  f.after_build do |b|
    b.menu_items = [Factory.build(:menu_item, :booking => b)]
  end
end

Note the way we add child records in the after_build block, and how we're passing the current object to the factory. And now for the child model:

Factory.define(:menu_item) do |f|
  f.description "Roast Beef"
  f.sale_price 8.00
  f.association :booking
end

The problem with my colleague's implementation was the build message didn't include the booking attribute, therefore factory_girl was creating another Booking object to meet the MenuItem's association specification. So each time the factory was run, two Bookings were created, and the MenuItem was associated with the wrong record, not the one returned by the factory.

Now that we've specified our implementations correctly, we can build both Bookings and MenuItems individually.

Much head scratching was caused by that one—hopefully this refresher will save someone the same!