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

时间:2016-04-05 10:19:14

标签: ruby-on-rails factory-bot

我的国家工厂看起来像这样

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未正确运行,因此数据库未在测试之间重置为干净状态。因此,我很快就遇到了重复。

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

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

3 个答案:

答案 0 :(得分:2)

当事情必须是独特的时候,我倾向于使用序列。对于国家/地区代码,您需要枚举可能的国家/地区代码。一种方法是

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

答案 1 :(得分:2)

  

运行大型测试时,

你最多(可能会少一些)26^2 = 676可能的alpha组合,所以如果large tests意味着更多,那么就无法解决它。

为了论证,我认为它不会那样。

你可以从'AA'开始并'增加'就像这样:

  

AB AC ... AZ BA BB BC ...等

要做到这一点,你可以玩工厂女孩的序列(使用模数和分数):

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

答案 2 :(得分:0)

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

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