如何在 FactoryBot 中使用 has_many 关联设置工厂

发布于 2024-11-28 07:52:50 字数 1725 浏览 2 评论 0原文

有人可以告诉我我是否以错误的方式进行设置吗?

我有以下具有 has_many.through 关联的模型:

class Listing < ActiveRecord::Base
  attr_accessible ... 

  has_many :listing_features
  has_many :features, :through => :listing_features

  validates_presence_of ...
  ...  
end


class Feature < ActiveRecord::Base
  attr_accessible ...

  validates_presence_of ...
  validates_uniqueness_of ...

  has_many :listing_features
  has_many :listings, :through => :listing_features
end


class ListingFeature < ActiveRecord::Base
  attr_accessible :feature_id, :listing_id

  belongs_to :feature  
  belongs_to :listing
end

我正在使用 Rails 3.1.rc4、FactoryGirl 2.0.2、factory_girl_rails 1.1.0 和 rspec。这是我对 :listing 工厂的基本 rspec rspec 健全性检查:

it "creates a valid listing from factory" do
  Factory(:listing).should be_valid
end

这是 Factory(:listing)

FactoryGirl.define do
  factory :listing do
    headline    'headline'
    home_desc   'this is the home description'
    association :user, :factory => :user
    association :layout, :factory => :layout
    association :features, :factory => :feature
  end
end

:listing_feature:feature 工厂是类似的设置。
如果 association :features 行被注释掉,那么我的所有测试都会通过。
当它是

association :features, :factory => :feature

错误消息是 # 的未定义方法“each”,我认为这对我来说有意义,因为 listing.features 返回一个数组。所以我将其更改为

association :features, [:factory => :feature]

,现在收到的错误是 ArgumentError: Not Registration: features 以这种方式生成工厂对象是不明智的,还是我错过了什么?非常感谢您的所有意见!

Can someone tell me if I'm just going about the setup the wrong way?

I have the following models that have has_many.through associations:

class Listing < ActiveRecord::Base
  attr_accessible ... 

  has_many :listing_features
  has_many :features, :through => :listing_features

  validates_presence_of ...
  ...  
end


class Feature < ActiveRecord::Base
  attr_accessible ...

  validates_presence_of ...
  validates_uniqueness_of ...

  has_many :listing_features
  has_many :listings, :through => :listing_features
end


class ListingFeature < ActiveRecord::Base
  attr_accessible :feature_id, :listing_id

  belongs_to :feature  
  belongs_to :listing
end

I'm using Rails 3.1.rc4, FactoryGirl 2.0.2, factory_girl_rails 1.1.0, and rspec. Here is my basic rspec rspec sanity check for the :listing factory:

it "creates a valid listing from factory" do
  Factory(:listing).should be_valid
end

Here is Factory(:listing)

FactoryGirl.define do
  factory :listing do
    headline    'headline'
    home_desc   'this is the home description'
    association :user, :factory => :user
    association :layout, :factory => :layout
    association :features, :factory => :feature
  end
end

The :listing_feature and :feature factories are similarly setup.
If the association :features line is commented out, then all my tests pass.
When it is

association :features, :factory => :feature

the error message is
undefined method 'each' for #<Feature> which I thought made sense to me because because listing.features returns an array. So I changed it to

association :features, [:factory => :feature]

and the error I get now is ArgumentError: Not registered: features Is it just not sensible to be generating factory objects this way, or what am I missing? Thanks very much for any and all input!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

红衣飘飘貌似仙 2024-12-05 07:52:50

或者,您可以使用块并跳过 association 关键字。这使得无需保存到数据库即可构建对象(否则,即使您使用 build 函数而不是 create,has_many 关联也会将您的记录保存到数据库中)。

FactoryGirl.define do
  factory :listing_with_features, :parent => :listing do |listing|
    features { build_list :feature, 3 }
  end
end

Alternatively, you can use a block and skip the association keyword. This makes it possible to build objects without saving to the database (otherwise, a has_many association will save your records to the db, even if you use the build function instead of create).

FactoryGirl.define do
  factory :listing_with_features, :parent => :listing do |listing|
    features { build_list :feature, 3 }
  end
end
陌伤浅笑 2024-12-05 07:52:50

创建此类关联需要使用 FactoryGirl 的回调。

可以在这里找到一组完美的示例。

https://thoughtbot.com/blog/aint-no-calla-back-girl

将其带入您的示例中。

Factory.define :listing_with_features, :parent => :listing do |listing|
  listing.after_create { |l| Factory(:feature, :listing => l)  }
  #or some for loop to generate X features
end

Creating these kinds of associations requires using FactoryGirl's callbacks.

A perfect set of examples can be found here.

https://thoughtbot.com/blog/aint-no-calla-back-girl

To bring it home to your example.

Factory.define :listing_with_features, :parent => :listing do |listing|
  listing.after_create { |l| Factory(:feature, :listing => l)  }
  #or some for loop to generate X features
end
离不开的别离 2024-12-05 07:52:50

您可以使用trait

FactoryGirl.define do
  factory :listing do
    ...

    trait :with_features do
      features { build_list :feature, 3 }
    end
  end
end

如果您需要创建数据库,则使用callback

...

trait :with_features do
  after(:create) do |listing|
    create_list(:feature, 3, listing: listing)
  end
end

在您的规范中使用如下所示:

let(:listing) { create(:listing, :with_features) }

这将消除工厂中的重复并更具可重用性。

https://robots.thoughtbot.com/remove-duplication-with-factorygirls-traits

You could use trait:

FactoryGirl.define do
  factory :listing do
    ...

    trait :with_features do
      features { build_list :feature, 3 }
    end
  end
end

With callback, if you need DB creation:

...

trait :with_features do
  after(:create) do |listing|
    create_list(:feature, 3, listing: listing)
  end
end

Use in your specs like this:

let(:listing) { create(:listing, :with_features) }

This will remove duplication in your factories and be more reusable.

https://robots.thoughtbot.com/remove-duplication-with-factorygirls-traits

夏雨凉 2024-12-05 07:52:50

我尝试了几种不同的方法,这是对我来说最可靠的方法(适合您的情况)

FactoryGirl.define do
  factory :user do
    # some details
  end

  factory :layout do
    # some details
  end

  factory :feature do
    # some details
  end

  factory :listing do
    headline    'headline'
    home_desc   'this is the home description'
    association :user, factory: :user
    association :layout, factory: :layout
    after(:create) do |liztng|
      FactoryGirl.create_list(:feature, 1, listing: liztng)
    end
  end
end

I tried a few different approaches and this is the one that worked most reliably for me (adapted to your case)

FactoryGirl.define do
  factory :user do
    # some details
  end

  factory :layout do
    # some details
  end

  factory :feature do
    # some details
  end

  factory :listing do
    headline    'headline'
    home_desc   'this is the home description'
    association :user, factory: :user
    association :layout, factory: :layout
    after(:create) do |liztng|
      FactoryGirl.create_list(:feature, 1, listing: liztng)
    end
  end
end
妄断弥空 2024-12-05 07:52:50

从 FactoryBot v5 开始,关联保留了构建策略。关联是解决此问题的最佳方法,文档提供了很好的示例it

FactoryBot.define do
  factory :post do
    title { "Through the Looking Glass" }
    user
  end

  factory :user do
    name { "Taylor Kim" }

    factory :user_with_posts do
      posts { [association(:post)] }
    end
  end
end

或者控制计数:

    transient do
      posts_count { 5 }
    end

    posts do
      Array.new(posts_count) { association(:post) }
    end

Since FactoryBot v5, associations preserve build strategy. Associations are the best way to solve this and the docs have good examples for it:

FactoryBot.define do
  factory :post do
    title { "Through the Looking Glass" }
    user
  end

  factory :user do
    name { "Taylor Kim" }

    factory :user_with_posts do
      posts { [association(:post)] }
    end
  end
end

Or with control over the count:

    transient do
      posts_count { 5 }
    end

    posts do
      Array.new(posts_count) { association(:post) }
    end
两个我 2024-12-05 07:52:50

与 @thisismydesign 类似,但它在我这边创建了一个额外的 post (FactoryBot v6.2)。

为了避免这种情况,我添加了关键字 instance ,如下所示:

FactoryBot.define do
  factory :post do
    title { "Through the Looking Glass" }
    user
  end

  factory :user do
    name { "Taylor Kim" }

    factory :user_with_posts do
      posts { [association(:post, user: instance)] }
    end
  end
end

Similar to @thisismydesign, however it created an additional post on my end (FactoryBot v6.2).

To avoid this situation, I've added keyword instance as below:

FactoryBot.define do
  factory :post do
    title { "Through the Looking Glass" }
    user
  end

  factory :user do
    name { "Taylor Kim" }

    factory :user_with_posts do
      posts { [association(:post, user: instance)] }
    end
  end
end
生活了然无味 2024-12-05 07:52:50

这是我的设置方式:

# Model 1 PreferenceSet
class PreferenceSet < ActiveRecord::Base
  belongs_to :user
  has_many :preferences, dependent: :destroy
end

#Model 2 Preference

class Preference < ActiveRecord::Base    
  belongs_to :preference_set
end



# factories/preference_set.rb

FactoryGirl.define do
  factory :preference_set do
    user factory: :user
    filter_name "market, filter_structure"

    factory :preference_set_with_preferences do
      after(:create) do |preference|
        create(:preference, preference_set: preference)
        create(:filter_structure_preference, preference_set: preference)
      end
    end
  end

end

# factories/preference.rb

FactoryGirl.define do
  factory :preference do |p|
    filter_name "market"
    filter_value "12"
  end

  factory :filter_structure_preference, parent: :preference do
    filter_name "structure"
    filter_value "7"
  end
end

然后在您的测试中您可以执行以下操作:

@preference_set = FactoryGirl.create(:preference_set_with_preferences)

希望有所帮助。

Here is how I set mine up:

# Model 1 PreferenceSet
class PreferenceSet < ActiveRecord::Base
  belongs_to :user
  has_many :preferences, dependent: :destroy
end

#Model 2 Preference

class Preference < ActiveRecord::Base    
  belongs_to :preference_set
end



# factories/preference_set.rb

FactoryGirl.define do
  factory :preference_set do
    user factory: :user
    filter_name "market, filter_structure"

    factory :preference_set_with_preferences do
      after(:create) do |preference|
        create(:preference, preference_set: preference)
        create(:filter_structure_preference, preference_set: preference)
      end
    end
  end

end

# factories/preference.rb

FactoryGirl.define do
  factory :preference do |p|
    filter_name "market"
    filter_value "12"
  end

  factory :filter_structure_preference, parent: :preference do
    filter_name "structure"
    filter_value "7"
  end
end

And then in your tests you can do:

@preference_set = FactoryGirl.create(:preference_set_with_preferences)

Hope that helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文