强制工厂内 2 字符字段唯一性的最佳方法?

Best way to force uniqueness of a 2-character field within a factory?

我有一个看起来像这样的乡村工厂

FactoryGirl.define do
  factory :country do 
    name { "Country_Name_#{rand(10000).to_s}#{('a'..'z').to_a.shuffle.join.first(10)}" }
    alpha { ('a'..'z').to_a.shuffle.join.first(2) } 
  end
end

alpha 是一个 2 个字母的国家代码,我验证了它的唯一性。

validates :alpha,
  presence: true,
  uniqueness:true,
  length: {is: 2} 

在运行宁大测试的时候,我经常运行进入

ActiveRecord::RecordInvalid:
       Validation failed: Alpha has already been taken

特别是在创建子记录时

factory :state do 
  name { "State_Name_#{rand(10000).to_s}#{('a'..'z').to_a.shuffle.join.first(10)}" }
  country
end

有没有更好的方法来构建我的国家工厂以避免重复?或者有没有更好的方法来构建国家工厂,以便 belongs_to 国家是新记录和现有记录的混合体

解决方案

对于其他遇到类似问题的人来说,序列绝对是正确的选择(感谢 born4new 和 Slicedpan)。

在实现这个过程中,我意识到我还有一些其他问题导致了问题。

首先,Database Cleaner 未正确 运行ning,因此数据库未在两次测试之间重置为干净状态。因此,我很快就 运行 重复了一遍。

我还发现我需要在每次测试后重置 FactoryGirl 序列。否则,对于长序列,我会看到 zx, zy, zz, a, b,并且单个字符无法通过我的验证。

#spec/rails_helper.rb
config.append_after(:each) do
  DatabaseCleaner.clean
  FactoryGirl.reload
end

当事物必须独一无二时,我倾向于使用序列。对于国家代码,您需要枚举可能的国家代码。一种方式如下

sequence :country_code do |n|  
  i1 = n % 26
  i2 = n / 26
  chars = ('a'..'z').to_a
  "#{chars[i1]}#{chars[i2]}"
end

然后在你的工厂里你可以简单地做

factory :country do 
  name { "Country_Name_#{rand(10000).to_s}#{('a'..'z').to_a.shuffle.join.first(10)}" }
  alpha { generate :country_code } 
end

When running large tests,

你最多(可能少一点)26^2 = 676 可能的 alpha 组合,所以如果 large tests 意味着更多,就没有办法解决它。

为了论证,我假设它小于那个。

您可以从 'AA' 和 'increment' 开始,像这样:

AB AC ... AZ BA BB BC ...etc

为此,您可以玩工厂女孩的序列(使用模数和除法):

sequence(:alpha) do |n|
  ('a'..'z').to_a[n / 26] + ('a'..'z').to_a[n % 26]
end

这比使用序列效率低(因为它需要读取数据库),但效果很好:

FactoryGirl.define do
  factory :country do 
    alpha do
      loop do
        possible_state = ('a'..'z').to_a.sample(2).join
        break possible_state unless Country.exists?(alpha: possible_state)
      end
    end
  end
end

还要注意 sample.

的使用