FactoryGirl 和 RSpec:为规格创建一条记录,要求 nested_attributes

FactoryGirl and RSpec: create a record with required nested_attributes for specs

我有一个模型 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 关联。所以基本上它应该创建一个 location 和一个 team 记录。

如何创建与工厂的联系记录,这实际上创建了关联的 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

如果 difficult/not 可以用 factory_girl 做到这一点:我怎样才能直接用 rspec 做到这一点?问题是我无法创建 contacts_team 记录或 relationship 记录,因为它关联的 contact 尚不存在!而且我无法创建 contact 记录,因为关联的 contacts_team 记录或 relationship 记录尚不存在。看来我被困住了,但必须有一种不马虎的方法来做到这一点。

我上周刚有类似的需求。

在你这个工厂的最后,你可以打电话给下一个工厂,然后他们就会关联起来。例如:

/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)

您可以只使用 factory girl 创建一个联系人,即使您只需要一个位置,例如,因为所有内容都会立即生成。

备选方案 (rspec)

不要"chain"你的工厂,而是在/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 个位置)

希望这可以帮助您找出正确的模式来制作您的测试数据:)

基本上,您可以通过多种方式进行。但是经常被建议的一种方法是使用 FactoryGirl after(:create)after(:build)。在您的情况下,如果您拥有所有型号的工厂,您可以轻松调用:

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

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

答案在这里。我们需要构建关联记录(contacts_team 记录和 relationship 记录),然后我们将所有记录同时保存到数据库(就像 [=25 如何保存嵌套属性一样) =]):

#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 记录和关联的 relationship记录。