Using a 'default' trait in FactoryGirl to avoid unnecessary association creation

4.1k views Asked by At

Is it possible to define a default trait in FactoryGirl? If I define a factory like this (where both question_response belongs_to question):

factory :question_response do
  question
  work_history

  trait :open do
    question { FactoryGirl.create :question, question_type: 'open' }
  end
end

When I do FactoryGirl.create :question_response, :open it will first create a default question and then create another inside the trait, which is an unnecessary operation.

Ideally I'd like to do this:

factory :question_response do
  work_history

  trait :default do
    question { FactoryGirl.create :question, question_type: 'yes_no' }
  end

  trait :open do
    question { FactoryGirl.create :question, question_type: 'open' }
  end
end

And then doing FactoryGirl.create :question will use the default trait, but it doesn't seem to be possible.

3

There are 3 answers

1
kuboon On BEST ANSWER

When I do FactoryGirl.create :question_response, :open it will first create a default question and then create another inside the trait

It's not true. if you specify the trait with question, it will overwrite the factory behavior before creation so that it does not create a default question.

I checked it with FactoryGirl v4.5.0

0
Alex P On

Just in case someone else is looking for 'default trait' scenario, this was discussed with examples in https://github.com/thoughtbot/factory_bot/issues/528#issuecomment-18289143

Basically, you can define trait with defaults and then use it for different factories. And then you can use appropriate factory with needed configuration.

For example:

FactoryBot.define do
  trait :user_defaults do
    email { Faker::Internet.unique.email }
    username { Faker::Internet.unique.username }
    password { "test1234" }
  end

  factory :with_admin_role, class: User do
    user_defaults

    after(:create) do |user, _|
      user.add_role :admin
    end
  end

  factory :with_readonly_role, class: User do
    user_defaults

    after(:create) do |user, _|
      user.add_role :readonly
    end
  end
end
0
patrick On

Your trait is creating a 2nd record because you have a block that is creating a record:

trait :open do
  question { FactoryGirl.create :question, question_type: 'open' }
end

Instead, you can do define a trait on question that has the question type set, and then have your question_response use that question with the open trait as its default.

factory.define :question do
  trait :open do
    question_type 'open'
  end
end

factory.define :question_response do
  association :question, :open
end