Как создать имеет и принадлежит к многим ассоциациям в Factory girl


учитывая следующее

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

как вы определяете фабрики для компаний и пользователей, включая двунаправленную ассоциацию? Вот моя попытка

Factory.define :company do |f|
  f.users{ |users| [users.association :company]}
end

Factory.define :user do |f|
  f.companies{ |companies| [companies.association :user]}
end

Теперь я стараюсь

Factory :user

возможно, неудивительно, что это приводит к бесконечному циклу, поскольку фабрики рекурсивно используют друг друга для определения себя.

более удивительно, что я не нашел упоминания о том, как это сделать в любом месте, есть ли шаблон для определения необходимого фабрики или я делаю что-то принципиально неправильное?

11 117

11 ответов:

вот решение, которое работает для меня.

FactoryGirl.define do

  factory :company do
    #company attributes
  end

  factory :user do
   companies {[FactoryGirl.create(:company)]}
   #user attributes
  end

end

Если вам понадобится конкретная компания, вы можете использовать завод таким образом

company = FactoryGirl.create(:company, #{company attributes})
user = FactoryGirl.create(:user, :companies => [company])

надеюсь, это будет полезно для кого-то.

Factorygirl с тех пор был обновлен и теперь включает обратные вызовы для решения этой проблемы. Взгляните на http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl для получения дополнительной информации.

на мой взгляд, просто создайте две разные фабрики, такие как:

 Factory.define :user, :class => User do |u|
  # Just normal attributes initialization
 end

 Factory.define :company, :class => Company do |u|
  # Just normal attributes initialization
 end

когда вы пишете тест-кейсы для пользователя, то просто напишите так

 Factory(:user, :companies => [Factory(:company)])

надеюсь, что это будет работать.

Я не смог найти пример для вышеупомянутого случая на предоставленном веб-сайте. (Только 1: N и полиморфные ассоциации, но не habtm). У меня был похожий случай, мой код выглядит так:

Factory.define :user do |user|
 user.name "Foo Bar"
 user.after_create { |u| Factory(:company, :users => [u]) }
end

Factory.define :company do |c|
 c.name "Acme"
end

то, что работало для меня было установление связи при использовании фабрики. Используя Ваш пример:

user = Factory(:user)
company = Factory(:company)

company.users << user 
company.save! 
  factory :company_with_users, parent: :company do

    ignore do
      users_count 20
    end

    after_create do |company, evaluator|
      FactoryGirl.create_list(:user, evaluator.users_count, users: [user])
    end

  end

предупреждение: изменить пользователей: [пользователь] на :Пользователи => [пользователь] для ruby 1.8.x

нашел этот способ хороший и verbose:

FactoryGirl.define do
  factory :foo do
    name "Foo" 
  end

  factory :bar do
    name "Bar"
    foos { |a| [a.association(:foo)] }
  end
end

прежде всего я настоятельно рекомендую вам использовать has_many: through вместо habtm (подробнее об этом здесь), Так что вы будете в конечном итоге с что-то вроде:

Employment belongs_to :users
Employment belongs_to :companies

User has_many :employments
User has_many :companies, :through => :employments 

Company has_many :employments
Company has_many :users, :through => :employments

после этого у вас будет ассоциация has_many с обеих сторон и вы можете назначить их в factory_girl так, как вы это сделали.

обновление для Rails 5:

вместо has_and_belongs_to_many ассоциация, вы должны рассмотреть:

вы можете определить новую фабрику и использовать after(:create) обратный вызов для создания списка ассоциаций. Давайте посмотрим, как это сделать в этом примере:

FactoryBot.define do

  # user factory without associated companies
  factory :user do
    # user attributes

    factory :user_with_companies do
      transient do
        companies_count 10
      end

      after(:create) do |user, evaluator|
        create_list(:companies, evaluator.companies_count, user: user)
      end
    end
  end
end

атрибут companies_count является переходным и доступен в атрибутах фабрики и в обратном вызове через оценщик. Теперь вы можете создать пользователя с компаниями с возможностью указать, сколько компаний вы хотите:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15

для HABTM я использовал черты и обратные вызовы.

скажем, у вас есть следующие модели:

class Catalog < ApplicationRecord
  has_and_belongs_to_many :courses
  …
end
class Course < ApplicationRecord
  …
end

Вы можете определите фабрику выше:

FactoryBot.define do
  factory :catalog do
    description "Catalog description"
    …

    trait :with_courses do
      after :create do |catalog|
        courses = FactoryBot.create_list :course, 2

        catalog.courses << courses
        catalog.save
      end
    end
  end
end