Faker está produzindo dados duplicados quando usado em factory_girl

86

Estou tentando preencher alguns dados falsos em uma fábrica usando a gema Faker:

Factory.define :user do |user|
  user.first_name Faker::Name::first_name
  user.last_name Faker::Name::last_name
  user.sequence(:email) {|n| "user#{n}@blow.com" }
end

No entanto, embora eu espere que isso produza usuários com nomes e sobrenomes diferentes, cada um é o mesmo:

>> Factory(:user)
=> #<User id: 16, email: "[email protected]", created_at: "2011-03-18 18:29:33",     
updated_at: "2011-03-18 18:29:33", first_name: "Bailey", last_name: "Durgan">
>> Factory(:user)
=> #<User id: 17, email: "[email protected]", created_at: "2011-03-18 18:29:39", 
updated_at: "2011-03-18 18:29:39", first_name: "Bailey", last_name: "Durgan">

Como faço para que a gema Faker gere novos nomes para cada usuário e não apenas reutilize os originais?

Peter Nixey
fonte
1
Apenas um tiro no escuro, mas você já tentou usar algo assim user.sequence(:first_name} {|n| Faker::Name::first_name}? O FactoryGirl provavelmente está apenas avaliando sua chamada Faker quando carrega seus "acessórios". Usar o sequence param,&blockmétodo deve evitar isso.
Steven

Respostas:

156
Factory.define :user do |user|
  user.first_name { Faker::Name::first_name }
  user.last_name { Faker::Name::last_name }
  user.sequence(:email) {|n| "user#{n}@blow.com" }
end

Tente colocar colchetes ao redor dos falsificadores. veja este link

Will Ayd
fonte
8
Eu amo muito o stackoverflow - obrigado Will, você salvou meu bacon
Peter Nixey,
Obrigado, isso corrigiu meu problema!
Oranges13
5
Porque porque porque? O que está acontecendo aqui?
jordanpg
4
por causa do "atributo preguiçoso", consulte: github.com/thoughtbot/factory_girl/blob/master/…
Siwei Shen 申思维
9
Infelizmente, isso nem sempre funciona. Isso basicamente obtém um novo objeto faker aleatório; no entanto, por causa do RNG, ainda há uma chance de falhar.
Michael Lynch
45

Observe que o Faker ainda pode estar fornecendo dados duplicados devido à quantidade limitada de dados falsos disponíveis.

Para fins de teste simples e para obter validações de exclusividade, usei o seguinte:

sequence(:first_name) {|n| Faker::Name::first_name + " (#{n})"}
sequence(:last_name) {|n| Faker::Name::last_name + " (#{n})"}
TwiceB
fonte
3
Esta resposta merece mais votos positivos. É provável que aconteça quando seu teste criar muitas instâncias.
Enrico Carlesso
Sim, concordo com Enrico. +1
karlingen
Boa ideia, mas adicionar parênteses pode quebrar o resto se você usar nome e sobrenome para gerar emails, ou se você tiver validações no formato (não conheço nenhum nome que tenha parênteses: P).
Cyril Duchon-Doris,
18

Para preservar a resposta correta, aqui ela foi translocada do blog, não levo nenhum crédito pela resposta.

Se você usar o código abaixo, o faker não produzirá nomes exclusivos

Factory.define :user do |u|
  u.first_name Faker::Name.first_name
  u.last_name Faker::Name.last_name
end

No entanto, colocar chaves em volta do faker faz com que funcione!

Factory.define :user do |u|
  u.first_name { Faker::Name.first_name }
  u.last_name { Faker::Name.last_name }
end

Para explicar por quê, o primeiro exemplo está produzindo os mesmos nomes. Está avaliando apenas uma vez. O segundo exemplo avalia cada vez que a fábrica é usada.

Isso se deve ao {}fornecimento de avaliação preguiçosa. Essencialmente, eles estão fornecendo um proc / lambda com a chamada Faker como seu valor de retorno.

ocodo
fonte
Obrigado por postar isso. Não consegui descobrir por que o Faker não foi capaz de gerar dados aleatórios e parecia que todos os exemplos que encontrei mostravam como usar o sequenciamento, o que parecia estranho para mim. Eu queria usar o Faker para que cada registro fosse aleatório, não sequenciado. Apenas adicionar colchetes em torno das minhas chamadas Faker resolveu o problema. Simples e elegante!
Blimey85 de
5

Uma alternativa (menos eficiente) ao uso de sequências quando você tem uma validação de exclusividade em um atributo é verificar se um valor proposto já existe e continuar tentando novos até que seja exclusivo:

FactoryGirl.define do
  factory :company do
    name do
      loop do
        possible_name = Faker::Company.name
        break possible_name unless Company.exists?(name: possible_name)
      end
    end
  end
end
Dan Kohn
fonte