I want to DRY up the after create/build hooks in my Factory:
FactoryGirl.define do
  factory :poll do
    sequence :title do |n|
      "MyPollTitle#{n}"
    end
    sequence :description do |n|
      "MyPollDescription#{n}"
    end
    user
    factory :poll_with_answers do
      ignore do
        answers_count 2
      end
      after(:build) do |poll, evaluator|
        evaluator.answers_count.times do
          poll.answers << build(:answer, poll: poll)
        end
      end
      after(:create) do |poll, evaluator|
        evaluator.answers_count.times do
          poll.answers << create(:answer, poll: poll)
        end
      end
    end
  end
end
The problem I was facing is that it seems I can't define methods in FG? Idea how to DRY this up?
Firstly, after(:create) implicitly calls after(:build), at least in recent versions of FactoryGirl:
after(:build) - called after a factory is built (via FactoryGirl.build, FactoryGirl.create)
https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#callbacks
So in your case, the following code should be sufficient:
after(:build) do |poll, evaluator|
  evaluator.answers_count.times do
    poll.answers << build(:answer, poll: poll)
  end
end
However, after(:build) isn't triggered when you use build_stubbed() instead of build(), which is what I was trying to do when I came across this thread. To DRY up this code, it turns out you can use the callback() method to call the same block for multiple methods:
factory :user do
  callback(:after_build, :after_stub) do |user|
    do_something_with(user)
  end
end
This might be a cheap trick, but you can create a lambda in the second factory:
factory :poll_with_answers do
  ignore do
    answers_count 2
  end
  make_answers = lambda do |poll, evaluator, method|
    evaluator.answers_count.times do
      poll.answers << send(method, :answer, poll: poll)
    end
  end
  after(:build) do |poll, evaluator|
    make_answers.call poll, evaluator, :build
  end
  after(:create) do |poll, evaluator|
    make_answers.call poll, evaluator, :create
  end
end
I'm not at all happy with this pattern, but at least it DRY's stuff up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With