FactoryGirl和RSpec:使用规范的必需nested_attributes创建记录

时间:2016-03-24 18:33:26

标签: ruby-on-rails ruby rspec factory-bot

我的模型contact has_many :locations, through: :relationships以及has_many :teams, through: :contacts_teams

联系人必须拥有关联的teamlocation才能通过验证。换句话说:新的contact必须具有关联的relationship记录和关联的contacts_team记录。以下是模型:

#models/contact.rb
class Contact < ActiveRecord::Base
  has_many :contacts_teams
  has_many :teams, through: :contacts

  has_many :relationships, dependent: :destroy
  has_many :locations, through: :relationships

  accepts_nested_attributes_for :contacts_teams, allow_destroy: true

  # upon create, validate that at least one associated team and one associated location exist
  validate :at_least_one_contacts_team
  validate :at_least_one_relationship

  private

  def at_least_one_contacts_team
    return errors.add :base, "Must have at least one Team" unless contacts_teams.length > 0
  end

  def at_least_one_relationship
    return errors.add :base, "Must have at least one Location" unless relationships.length > 0
  end
end

#models/contacts_team.rb
class ContactsTeam < ActiveRecord::Base
  belongs_to :contact
  belongs_to :team
end

#models/team.rb
class Team < ActiveRecord::Base
  has_many :contacts_teams
  has_many :contacts, through: :contacts_teams
end

#models/relationship.rb
class Relationship < ActiveRecord::Base
  belongs_to :contact
  belongs_to :location
end

#models/location.rb
class Location < ActiveRecord::Base
  has_many :relationships
  has_many :contacts, through: :relationships
end

进行测试:使用factory_girl我想创建一个能够成功创建contact记录的contact工厂。由于每个contact记录都需要关联的contacts_team记录和relationship记录:当我创建contact记录时,也应创建这些记录。同样:contacts_team记录应与其关联的现有team,并且relation记录应与其关联的现有location。所以基本上它应该创建locationteam记录。

如何创建与工厂的联系人记录,这实际上会创建关联的contacts_team记录和relationship记录?

以下是我目前的工厂:

FactoryGirl.define do
  factory :contact do
    first_name "Homer"
    last_name "Simpson"
    title "Nuclear Saftey Inspector"
  end
end

FactoryGirl.define do
  factory :contacts_team do
  end
end

FactoryGirl.define do
  factory :team do
    name "Safety Inspection Team"
  end
end

FactoryGirl.define do
  factory :relationship do
  end
end

FactoryGirl.define do
  factory :location do
    name "Safety Location"
  end
end

如果使用factory_girl很难/不可能这样做:我怎么能用直接的rspec做到这一点?问题在于我无法创建contacts_team条记录或relationship条记录,因为它与之关联的contact尚未存在!我无法创建contact记录,因为相关的contacts_team记录或relationship记录尚未存在。好像我被困了,但必须有办法做到这一点并不是马虎。

3 个答案:

答案 0 :(得分:1)

我上周才有类似的要求。

在工厂结束时,您可以致电下一家工厂,然后他们就会相关联。例如:

/spec/factories/contacts.rb

FactoryGirl.define do

    factory :contact do |c|
        first_name "Homer"
        last_name "Simpson"
        title "Nuclear Saftey Inspector"

        # now, call the other two factories
        relationship
        contacts_team
    end


    factory :contacts_team do
        # call the team factory
        team
    end


    factory :relationship do
        # call the location factory
        location
    end


    # define the team and location factories...

end

现在,在/spec/controllers/contacts_controller_spec.rb

contact = FactoryGirl.create(:contact)

您可以使用工厂女孩来创建联系人,即使您只需要一个位置,因为所有内容都会立即生成。

替代(rspec)

不要&#34;链&#34;你的工厂,而不是/spec/controllers/contacts_controller_spec.rb

contact = FactoryGirl.create(:contact)
# use .create_list(model, number, parent) to make children of a specific parent
contacts_team = FactoryGirl.create_list(:contacts_team, 3, :contact => contact)
relationship = FactoryGirl.create_list(:relationship, 3, :contact => contact)
team = FactoryGirl.create_list(:team, 3, :contacts_team => contacts_team)
location = FactoryGirl.create_list(:location, 3, :relationship => relationship)

这将创建一个联系人,包含3个contact_teams(包含3个团队),以及3个关系(包含3个位置)

希望这有助于您找出制作测试数据的正确模式:)

答案 1 :(得分:1)

这是答案。我们需要构建关联的记录(contacts_team记录和relationship记录),然后我们将所有记录保存在同一时间到数据库(就像rails保存嵌套属性的方式):

#factories/contact.rb
FactoryGirl.define do
  factory :contact do
    first_name "Homer"
    last_name "Simpson"
    title "Nuclear Saftey Inspector"
    agency
    contacts_teams {build_list :contacts_team, 1 }
    relationships {build_list :relationship, 1 }
  end
end

#factories/contacts_teams.rb
FactoryGirl.define do
  factory :contacts_team do
    team
  end
end

#factories/teams.rb
FactoryGirl.define do
  factory :team do
    name "Safety Inspection Team"
  end
end

#factories/relationships.rb
FactoryGirl.define do
  factory :relationship do
    location
  end
end

#factories/locations.rb  
FactoryGirl.define do
  factory :location do
    name "Safety Location"
  end
end

然后您需要做的就是:

create(:contact)

然后,它会立即创建contact记录,team记录,location记录,关联的contacts_team记录以及相关的{{1}记录。

答案 2 :(得分:0)

基本上,你可以采取多种方式。但通常建议的一种方法是使用FactoryGirl after(:create)after(:build)。在您的情况下,如果您的所有型号都有工厂,您可以轻松致电:

after(:create) do |team, evaluator| create_list(:contact_team, 5, team: team) end 根据文档,在(:create)之后产生团队和评估者,它们存储工厂的所有值,包括瞬态属性

这将为您创建5个与团队关联的contact_teams。对于每个工厂,如果您定义这样的所有关系,您将定义完整的关联。另一种方法是遵循诺姆的建议,这也是好的,但有其自身的局限性。 您可以在此处找到更多信息:FactoryGirl Associations