Skip to content


Complex Associations in Factory Girl

I finally decided to bite the bullet and move the main Rails project I work on from FixtureReplacement to Factory Girl. Weighing in at 7404 lines of test code, this was a non-trivial task.

If you peruse the Factory Girl docs, it mentions associations, but only how to handle a simple “belongs to” or maybe “has one” sort of association. My good friend led me to this thread on StackOverflow.

Basically, the user fowlduck came up with the solution of having an “after_build” block.

My problem couldn’t be solved with a simple “has_many/belongs_to” association, because in this case I needed to ensure the invoice seller matched the producer of the product being invoiced. Here was my solution:

Factory.define :invoice do |i|
  i.after_build do |invoice|
    invoice.buyer = Factory.create :consumer unless invoice.buyer
    next if invoice.seller
    invoice.line_items = [Factory.create(:line_item)]
    invoice.seller = invoice.line_items.first.product.producer
  end
end

So first, notice the “unless invoice.buyer” and “next if invoice.seller” these allow me to override these values in my tests. You’re gonna wanna keep that in mind. Next, I create a LineItem, which automatically creates a product, which in turn, creates a producer. After that I can just set the seller to be the producer of the product in the first line item. Voila, product line_item producer matches invoice seller.

I’ve found this tremendously useful. Here’s another example of how I’ve used it:

Factory.define :user do |a|
  name = Faker::Name.name
  a.real_name { name }
  a.username { Faker::Internet.user_name(name) }
  a.password 'secret-1'
  a.password_confirmation 'secret-1'
  a.email_address { Faker::Internet.email(name) }
  a.after_build do |login|
    next if login.valid?
    next unless login.errors.invalid?(:username)
    login.username = Faker::Internet.user_name(login.real_name)+String.random(4)
  end
end

I did this because, everyone once in a great while my tests would fail because the same username is used more than once. But now, if this happens, I just tack on 4 random characters. Since i did this, I haven’t had the test fail.

Posted in Rails, Ruby, Software Development, Tips and Tricks.

Tagged with , , , , , .


0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.



Some HTML is OK

or, reply to this post via trackback.