2011-08-16 1 views
32

'전자 메일'필드에 고유성 제약 조건이있는 'user'모델이 있다고 가정합니다.레일 : 팩토리 걸 (Factory Girl)에서 중복 오류 방지 ... 잘못 했나요?

일단 공장을 (: 사용자)라고 말하면 모든 것이 좋지만 두 번째 호출하면 '항목이 이미 있습니다'오류로 실패합니다.

나는 현재 도우미를 통해 공장을 만들기 전에 DB에서 기존 항목을 검색하는 간단한 도우미를 사용하고 있습니다.

효과가 있지만 완전히 우아하지는 않습니다.이 문제가 얼마나 일반적인 것으로 생각 하는지를 고려할 때 더 나은 해결책이 있다고 생각합니다. ... 그래서 factory 소녀가 factory를 return_or_create하는 대신에 create()로 미리 충전하는 대신 inbuilt 방법을 사용합니까? 그렇지 않다면, 대부분의 사람들은 어떻게 그들의 공장과 중복 된 항목을 피합니까?

+0

이 문제도 발생합니다. 전자 메일 필드에 시퀀스를 추가 했으므로 이론적으로 Factory (: user)에 전화 할 때마다 변경됩니다. 나는 그 자리에 있고 당신이 가진 문제에 아직도 달려있다. – someoneinomaha

+1

나는 같은 문제가 있었다. 나는 FactoryGirl이 비참하게 실패하고 아마도 예외 (아마도 정리를 피함)를 던졌던 이전 테스트에서 나의 테스트 데이터베이스에 나쁜 데이터를 남겼다는 것을 알아 차렸다. RAILS_ENV = 테스트 bin/rake db : drop RAILS_ENV = 테스트 bin/rake db : 만들기 RAILS_ENV = 테스트 bin/rake db : 마이그레이션 ' 이렇게하면 이전 데이터가 모두 지워집니다. 희망이 도움이 @ somomeinomaha – Joel

답변

69

간단한 답 : 사용 factory.sequence

당신은 당신이 같은 결코하지 않도록 factory_girl에 시퀀스를 추가 할 수 있습니다 고유해야 필드가있는 경우 :

Factory.define :user do |user| 
    sequence(:email){|n| "user#{n}@factory.com" } 
    user.password{ "secret" } 
end 

`[email protected] '과 같은 고유 한 전자 메일 주소를 생성하기 위해 매번 n이 증가합니다. 시간이 지남에

나는이 실제로 독특한 만들 수있는 가장 유용한 방법이 아니다 것으로 나타났습니다 ...

그러나이 항상 Rails.env.development에 큰되지 않습니다 (자세한 정보를 원하시면 https://github.com/thoughtbot/factory_girl/wiki/Usage 참조) 이메일 주소. 그 이유는 공장이 테스트 환경에 대해 항상 고유하지만 개발 환경에서는 항상 고유하지는 않으며 n은 환경을 시작하고 종료 할 때 자동으로 재설정되기 때문입니다. :test에서 이것은 데이터베이스가 지워지기 때문에 문제가되지 않지만 :development에서 잠시 동안 동일한 데이터를 사용하는 경향이 있습니다.

그런 다음 충돌이 발생하고 고유 한 성가신 이메일을 수동으로 무시해야합니다.

종종 더 유용는 : 나는 임의의 숫자를 생성하여 대신 이동 정기적으로 콘솔에서 u = Factory :user를 호출하기 때문에 임의의 숫자

를 사용합니다. 당신은 충돌을 피하기 위해 보장하지만, 실제로는 좀처럼 발생하지 않는 :

Factory.define :user do |user| 
    user.email {"user_#{Random.rand(1000).to_s}@factory.com" } 
    user.password{ "secret" } 
end 

N.B. FactoryGirl (https://github.com/thoughtbot/factory_girl/issues/219](see 여기)의 충돌 (버그?) 때문에 rand() 대신 Random.rand을 사용해야합니다.

이렇게하면 데이터베이스에 이미 팩토리 생성 사용자가 있는지 여부에 관계없이 명령 줄에서 자유롭게 사용자를 만들 수 있습니다. 이메일 테스트를 만들기위한

추가 선택 당신은 종종 특정 사용자에 의한 행동이 다른 사용자에게 이메일을 유발하는지 확인하려면 테스트 이메일에 도착하면

쉽게.

Robin Hood으로 로그인하고 Maid Marion으로 이메일을 보내신 후받은 편지함으로 이동하여 확인하십시오. 받은 편지함에 표시되는 내용은 [email protected]입니다. 대체 누구야?

전자 메일이 예상 한대로 송수신되었는지 여부를 확인하려면 데이터베이스로 돌아 가야합니다. 다시 이것은 약간의 고통입니다.

대신에 임의의 숫자와 결합 된 팩토리 사용자의 이름을 사용하여 이메일을 생성하는 것이 좋습니다. 이렇게하면 누가 어떤 일이 일어나고 있는지 쉽게 확인할 수 있습니다 (또한 충돌이 사라질 가능성이 거의 없습니다). .gsub(/[^a-zA-Z1-10]/, '')

: 위조자가 가끔 이메일 친화적되지 않습니다 (마이크 오도넬을) 우리가 허용되는 문자 허용 목록을 할 필요가 이름을 생성하기 때문에,

Factory.define :user do |user| 
    user.first_name { Faker::Name::first_name } 
    user.last_name { Faker::Name::last_name } 
    user.email {|u| "#{u.first_name}_#{u.last_name}_#{Random.rand(1000).to_s}@factory.com" } 
end 

마지막으로 우리가 얻을 이름을 생성하기 위해 위조자 보석 (http://faker.rubyforge.org/)를 사용하여

Factory.define :user do |user| 
    user.first_name { Faker::Name::first_name } 
    user.last_name { Faker::Name::last_name } 
    user.email {|u| "#{u.first_name.gsub(/[^a-zA-Z1-10]/, '')}_#{u.last_name.gsub(/[^a-zA-Z1-10]/, '')}_#{Random.rand(1000).to_s}@factory.com" } 
end 

이 우리에게 등 [email protected][email protected] 다음

+0

...또는 전자 메일 주소로'Faker :: Internet.email'을 사용하십시오. –

+2

그러나 전자 메일 주소가 이름과 다를 수 있다는 단점이 있습니다. 나는 당신이 여기에서하려고하는 것을 지금 본다. 또한 ffaker는 고전적인 Faker, FWIW보다 빠르고 더 잘 작동합니다. –

+1

그런 다음 이메일을 이름과 일치 시키려면'Faker :: Internet.email ("# {first_name} # {last_name}")'을 사용하십시오. –

10

같은 개인적하지만 고유의 이메일을 제공 내가 그 객체 '와 동일하게 내 공장 소녀의 순서로'N '을 강제로 할거야 의 ID 및함으로써 충돌을 피하기 :

def self.next_id 
    self.last.nil? ? 1 : self.last.id + 1 
end 

가 그럼 난 사양에서 User.next_id 전화 :

첫째, 나는 다음 ID가 응용 프로그램/모델/user.rb에 있어야 무엇을 발견하는 방법을 정의 /factories.rb를 입력하여 시퀀스를 시작하십시오.

factory :user do 
    association(:demo) 
    association(:location) 
    password "password" 
    sequence(:email, User.next_id) {|n| "darth_#{n}@sunni.ru" } 
end 
1

이 테스트는 항상 테스트가 통과하는 좋은 방법입니다. 그렇지 않으면 고유 한 전자 메일을 만드는 시간을 100 % 확신 할 수 없습니다. 당신은 단지 속성에 대한 몇 가지 값을 생성해야하는 경우

FactoryGirl.define do 
    factory :user do 
    name { Faker::Company.name } 
    email { generate(:email) } 
    end 
    sequence(:email) do 
    gen = "user_#{rand(1000).to_s}@factory.com" 
    while User.where(email: gen).exists? 
     gen = "user_#{rand(1000).to_s}@factory.com" 
    end 
    gen 
    end 
end 
0

은 또한 속성에 사용되는 이전의 문자열을 추적 문자열에 메서드를 추가 할 수 있습니다. 그러면 다음과 같이 할 수 있습니다.

factory :user do 
    fullname { Faker::Name.name.unique('user_fullname') } 
end 

이 방법을 시드에 사용합니다. 시퀀스 번호는 현실적으로 보이지 않기 때문에 피하고 싶었습니다. 여기

이런 일이 만드는 문자열 확장

:

class String 
    # Makes sure that the current string instance is unique for the given id. 
    # If you call unique multiple times on equivalent strings, this method will suffix it with a upcounting number. 
    # Example: 
    #  puts "abc".unique("some_attribute") #=> "abc" 
    #  puts "abc".unique("some_attribute") #=> "abc-1" 
    #  puts "abc".unique("some_attribute") #=> "abc-2" 
    #  puts "abc".unique("other") #=> "abc" 
    # 
    # Internal: 
    # We keep a data structure of the following format: 
    #  @@unique_values = { 
    #  "some_for_id" => { "used_string_1" : 1, "used_string_2": 2 } # the numbers represent the counter to be used as suffix for the next item 
    #  } 
    def unique(for_id) 
    @@unique_values ||= {} # initialize structure in case this method was never called before 
    @@unique_values[for_id] ||= {} # initialize structure in case we have not seen this id yet 
    counter = @@unique_values[for_id][self] || 0 
    result = (counter == 0) ? self : "#{self}-#{counter}" 
    counter += 1 
    @@unique_values[for_id][self] = counter 
    return result 
    end 

end 

주의 : 우리는 이전의 모든 문자열 (가능한 최적화)을 추적하기 때문에 이것은, 속성을 많이 사용하지 않아야합니다.

관련 문제