This website uses cookies for analytics and to improve provided services. By choosing I Accept, you consent to our use of them and other tracking technologies according to our Privacy Policy

FactoryGirl many-to-many setup

Recently, we were looking at how to set up a many-to-many relationship with FactoryGirl. Unfortunately, Google wasn’t all that helpful in solving this problem, so we had to figure out this issue on our own. We found that the only possible solution is to use the after_create callback as the example below shows:

FactoryGirl.define do
  factory :book do
    sequence(:title) { |n| "Book #{n}" }
  end

  factory :user do
    sequence(:email) { |n| "user#{n}@example.org" }
  end

  factory :user_with_book, parent: :user do
    after_create do |user|
      user.books << FactoryGirl.create(:book)
    end
  end
end

This solution wasn't enough for us though, because we also needed to share a book between two users. Our first attempt to set up this connection had to be done manually like this:

let(:book) { FactoryGirl.create(title: "The Winds of Winter") }
let(:jon) { FactoryGirl.create(:user) }
let(:arya) { FactoryGirl.create(:user) }

before do
  jon.books << book
  arya.books << book
end

After digging a bit deeper though, we discovered that to DRY this we can actually use the after_create callback too. Just use the lesser known ignore and evaluator pair like this:

FactoryGirl.define do
  factory :book do
    sequence(:title) { |n| "Book #{n}" }
  end

  factory :user do
    sequence(:email) { |n| "user#{n}@example.org" }
  end

  factory :user_with_book, parent: :user do
    ignore do
      book { FactoryGirl.create(:book) }
    end

    after_create do |user, evaluator|
      user.books << evaluator.book
    end
  end
end

Finally in specs we can easily set up users with the same book like this:

let(:book) { FactoryGirl.create(title: "The Winds of Winter") }
let(:jon) { FactoryGirl.create(:user_with_book, book: book) }
let(:arya) { FactoryGirl.create(:user_with_book, book: book) }

Now we have much cleaner code!

Confidently build your next app

Our devs are so communicative and diligent you’ll feel they are your in-house team. Work with experts who will push hard to understand your business and meet certain deadlines.

Talk to our team and confidently build your next big thing.

Pizza Hut Case Study: Introducing Contact-Free Delivery In Less Than 5 Hours
RoR2019 Why Ruby on Rails Is Still a Good Choice in 2020 [UPDATED]
template How to Prepare a Product Spec When Outsourcing Software Development