Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DRY FactoryGirl after create/build hooks

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?

like image 978
wintersolutions Avatar asked Jul 18 '12 05:07

wintersolutions


2 Answers

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
like image 67
Adrian Macneil Avatar answered Nov 02 '22 05:11

Adrian Macneil


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.

like image 28
Stefan Kanev Avatar answered Nov 02 '22 07:11

Stefan Kanev