chell
chell

Reputation: 7866

Why can't my factory definition use another one?

I am using the Rolify gem in a Rails 4.2 application.

I am using seed data in the factory which works. However, from what I have read, that's not the correct way to do it.

I'm having trouble getting my head around how to do so correctly without having to use complex factory calls in my spec files.

Here is my Employee factory:

require 'faker'

#Seed the database before running the tests only if in test mode
#Fixes problem with rake routes running this command and deleting out the database
Rails.application.load_seed if  Rails.env.test?

FactoryGirl.define do
  factory :employee do
    first_name { Faker::Name.first_name}
    last_name { Faker::Name.last_name}
    sequence(:email) { |n| "peterjohnson#{n}@example.com" }
    mobile 66816927867
    bio "MyText"
    address { Faker::Address.street_address}
    province_state { Faker::Address.state}
    country { Faker::Address.country}
    postal_code { Faker::Address.postcode}
    status :active
    bachelor_degree "B.Sc"
    password Faker::Internet.password(8)
    sequence(:paypal_email) { |n| "paypal_peterJohnson#{n}@example.com" }
    sequence(:skype_id) {|n| "peterjohnson_skype#{n}" }
    os :mac
    role_ids [Role.first.id]

    trait :proofreader do
        after(:create) {|employee| employee.add_role(:proofreader)}
    end

    trait :admin do
        after(:create) {|employee| employee.add_role(:admin)}
    end

    trait :super_admin do
        after(:create) {|employee| employee.add_role(:super_admin)}
    end
  end
end

I need to have the roles created before the Employee so that I can save their IDs in the Employee model.

In my application Roles are added via a form when an employee is registered. So I can't do an after create to add the role for some of the tests.

If I create the roles within the Employee factory as follows:

factory :employee do
  ["proofreader", "admin", "super_admin"].each do |role|
      FactoryGirl.create(:role, name: role)
  end
  first_name { Faker::Name.first_name}
  ...

I get an error telling me that it can't find the factory named role. It exists as follows:

FactoryGirl.define do
  factory :role do
    name :proofreader
  end
end

Here is my Employee model which validates that an Employee must have a role:

class Employee < ActiveRecord::Base
  # returns the full name of the employee. This code is found in a concern called name.rb
  include Name

  rolify

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable


  enum status: [:active, :vacation, :unemployed]

    enum os: [:mac, :windows]

    validates :first_name, 
            presence: true,
            length: {minimum: 2}

  validates :last_name, 
            presence: true,
            length: {minimum: 2}

    validates :email, email: true, presence: true, uniqueness: true
  validates :paypal_email, email: true, presence: true, uniqueness: true
  validates :skype_id, presence: true, uniqueness: true
  validates :mobile, presence: true, numericality: true, length: {minimum: 10}
  validates :address, :province_state, :country, :postal_code, :bachelor_degree, :os,:status, :role_ids,  presence: true 



end

So how can I create the Employee with a role without seeding the database?

Upvotes: 1

Views: 137

Answers (1)

Dave Schweisguth
Dave Schweisguth

Reputation: 37657

You're attempting to FactoryGirl.create(:role) when factory_girl reads the :employee factory definition. The :employee factory is in employee.rb and the :role factory is in role.rb (or something like that), and factory_girl probably reads factory definitions in alphabetical order by file name, so when :employee is defined :role doesn't exist yet.

You need to create all roles when the test runs, after all factories have been defined.

In the :employee factory, change how you initialize role_ids:

role_ids { [(Role.first || FactoryGirl.create(:role, name: :default)).id] }

(Actually you should just be able to set roles instead of role_ids so you don't have to call .id on the default role.)

Then add the additional role in a callback in a trait:

trait :proofreader do
  after(:create) do |employee|
    FactoryGirl.create :role, name: :proofreader
    employee.add_role(:proofreader)
  end
end

However, assuming your roles don't change except when your application code also changes, I'd use seeds. It would be a lot simpler, and I don't know why it would be incorrect. I've done it with seeds on several projects and it works well.

Upvotes: 1

Related Questions