为包含查询数据库的回调的 Rails 模型创建 FactoryGirl 工厂

Create FactoryGirl factory for Rails Model that Contains Callback that Queries DB

我定义了以下用户模型,在创建新用户后,默认记录被插入到用户和个性类型之间的连接 table 中(是的......我知道默认个性是老土):

class User < ActiveRecord::Base
  before_create :assign_default_personality_type

  has_many :personality_types, through: :personality_types_users
  has_many :personality_types_users, dependent: :destroy
  #using nested_forms gem
  accepts_nested_attributes_for :personality_types_users

  # When creating user, assign all to 'Loving' personality type by default
  def assign_default_personality_type
    if self.new_record?
      self.personality_types_attributes = [{ personality_type_id: PersonalityType.find_by_name('Loving').id }]
    end
  end

这是我的 personality_type 模型

class PersonalityType < ActiveRecord::Base

  has_many :users, through: :personality_types_users
  has_many :personality_types

我有一个连接模型 table class

class PersonalityTypesUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :personality_type

我的问题是通过 FactoryGirl 构建用户工厂。因为我有一个 before_create 回调链接到 User,当我尝试通过 FactoryGirl 建立一个新用户时,我收到以下错误:

Failure/Error: self.personality_types_attributes = [{ personality_type_id: PersonalityType.find_by_name('Loving').id }]

NoMethodError:
  undefined method `id' for nil:NilClass 

我明白了,当通过 FactoryGirl 构建用户时,测试数据库中没有 Loving 性格类型的数据。我 喜欢 就此事寻求帮助。这是我现有的工厂

FactoryGirl.define do
  factory :user do
    first_name { Faker::Name.first_name }
    last_name { Faker::Name.last_name }

  after(:build) do |em, evaluator|
    ...
  end


FactoryGirl.define do
  factory :personality_type do
    name { Faker::Lorem.characters(number: 6) }

  factory :loving_personality_type do
    name 'Loving'
  end

FactoryGirl.define do
  factory :personality_types_user do
    association :personality_type
    association :user
  end

总而言之,我的问题是如何在创建用户之前让 Loving personality_type 存在,以便在调用模型中的回调时,它有一个个性 ID找到了吗?

当我在用户模型中添加以下 FactoryGirl 回调时,似乎有点工作

after(:build) do |em, evaluator|
  loving_personality_type = PersonalityType.new(name: 'Loving')
  loving_personality_type.save
end

但这适用于独立的单元测试。当我 运行 整个规范模型文件时,它有很多测试,其中用户是在几个测试中构建和创建的,我得到

Failure/Error: loving_personality_type.save

 ActiveRecord::RecordNotUnique:
   PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_personality_types_on_name"
   DETAIL:  Key (name)=(Loving) already exists.
   : INSERT INTO "personality_types" ("name", "created_at", "updated_at") VALUES (, , ) RETURNING "id"

我期望 FactoryGirl 能够创建具有 'Loving' 个性类型关联的用户。我不想遍历整个代码库,在创建用户的每个 RSpec 文件中定义个性类型。感谢所有帮助。

你可以在工厂的after(:build)块中使用find_or_create_by

after(:build) do |em, evaluator|
   loving_personality_type = PersonalityType.find_or_create_by(name: 'Loving')
   loving_personality_type.save
end

如果数据库中存在,这基本上不会创建新的 PersonalityType。但是,return 它是 loving_personality_type 变量的实例。

在我看来,这应该可以解决您的问题。

您可以在这里阅读更多内容:https://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by