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

时间:2011-08-05 22:30:40

标签: rspec ruby-on-rails-3.1 has-many-through factory-bot

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

我有以下具有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

这是工厂(:上市)

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

错误消息是 我认为undefined method 'each' for #<Feature>对我有意义,因为listing.features会返回一个数组。所以我把它改成了

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

我现在得到的错误是ArgumentError: Not registered: features以这种方式生成工厂对象或者我缺少什么是不明智的?非常感谢任何和所有的输入!

5 个答案:

答案 0 :(得分:100)

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

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

答案 1 :(得分:54)

创建这些类型的关联需要使用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

答案 2 :(得分:20)

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

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

答案 3 :(得分:20)

您可以使用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

答案 4 :(得分:0)

以下是我设置的方法:

$ scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html
2016-09-08 18:13:12 [scrapy] INFO: Scrapy 1.1.2 started (bot: scrapybot)
2016-09-08 18:13:12 [scrapy] INFO: Spider opened
2016-09-08 18:13:13 [scrapy] DEBUG: Crawled (200) <GET http://doc.scrapy.org/en/latest/_static/selectors-sample1.html> (referer: None)
>>> response.selector.xpath('//title/text()')
[<Selector xpath='//title/text()' data=u'Example website'>]
>>> s = response.selector.xpath('//title/text()')[0]
>>> type(s)
<class 'scrapy.selector.unified.Selector'>
>>> type(s.root)
<type 'str'>
>>> s = response.selector.xpath('//title')[0]
>>> s.root
<Element title at 0x7fa95d3f1908>
>>> type(s.root)
<type 'lxml.etree._Element'>
>>> dir(s.root)
['__class__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '_init', 'addnext', 'addprevious', 'append', 'attrib', 'base', 'clear', 'cssselect', 'extend', 'find', 'findall', 'findtext', 'get', 'getchildren', 'getiterator', 'getnext', 'getparent', 'getprevious', 'getroottree', 'index', 'insert', 'items', 'iter', 'iterancestors', 'iterchildren', 'iterdescendants', 'iterfind', 'itersiblings', 'itertext', 'keys', 'makeelement', 'nsmap', 'prefix', 'remove', 'replace', 'set', 'sourceline', 'tag', 'tail', 'text', 'values', 'xpath']

>>> s.root.sourceline
4
>>> 

然后在你的测试中你可以做到:

# 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

希望有所帮助。