2011-08-22 11 views
50

Tengo un modelo de usuario que pertenece a un grupo. El grupo debe tener un atributo de nombre único. Fábrica de usuario y fábrica de grupo se definen como:Buscar o crear registro a través de la asociación factory_girl

Factory.define :user do |f| 
    f.association :group, :factory => :group 
    # ... 
end 

Factory.define :group do |f| 
    f.name "default" 
end 

Cuando se crea el primer usuario, también se crea un nuevo grupo. Cuando trato de crear un segundo usuario, falla porque quiere crear el mismo grupo nuevamente.

¿Hay alguna manera de decirle al método de asociación factory_girl que busque primero un registro existente?

Nota: Intenté definir un método para manejar esto, pero luego no puedo usar f.association. Me gustaría ser capaz de utilizarlo en escenarios de pepino así:

Given the following user exists: 
    | Email   | Group   | 
    | [email protected] | Name: mygroup | 

y esto sólo puede funcionar si la asociación se utiliza en la definición de fábrica.

Respuesta

16

Terminé usando una combinación de métodos se encuentran alrededor de la red, uno de ellos se hereda como sugerido por duckyfuzz en otra respuesta.

Yo siguiente:

# in groups.rb factory 

def get_group_named(name) 
    # get existing group or create new one 
    Group.where(:name => name).first || Factory(:group, :name => name) 
end 

Factory.define :group do |f| 
    f.name "default" 
end 

# in users.rb factory 

Factory.define :user_in_whatever do |f| 
    f.group { |user| get_group_named("whatever") } 
end 
1

Normalmente solo hago varias definiciones de fábrica. Uno para un usuario con un grupo y otro para un usuario sin grupo:

Factory.define :user do |u| 
    u.email "email" 
    # other attributes 
end 

Factory.define :grouped_user, :parent => :user do |u| 
    u.association :group 
    # this will inherit the attributes of :user 
end 

continuación, puede utilizar estos en sus definiciones paso para crear usuarios y grupos de éste por separado y unirlas a voluntad. Por ejemplo, puede crear un usuario agrupado y un usuario solitario y unir al usuario solitario al equipo de usuarios agrupados.

De todos modos, usted debe echar un vistazo a la pickle gem que permitirá escribir pasos como:

fábricas
Given a user exists with email: "[email protected]" 
And a group exists with name: "default" 
And the user: "[email protected]" has joined that group 
When somethings happens.... 
0

estoy usando exactamente el escenario de pepino que se describe en su pregunta:

Given the following user exists: 
    | Email   | Group   | 
    | [email protected] | Name: mygroup | 

puede extenderlo como:

Given the following user exists: 
    | Email   | Group   | 
    | [email protected] | Name: mygroup | 
    | [email protected] | Name: mygroup | 
    | [email protected] | Name: mygroup | 

Este creará 3 usuarios con el grupo "mygroup". Como se usa así utiliza la funcionalidad 'find_or_create_by', la primera llamada crea el grupo, las dos llamadas siguientes encuentran el grupo ya creado.

89

Usted puede utilizar initialize_with con find_or_create método

FactoryGirl.define do 
    factory :group do 
    name "name" 
    initialize_with { Group.find_or_create_by_name(name)} 
    end 

    factory :user do 
    association :group 
    end 
end 

También se puede utilizar con id

FactoryGirl.define do 
    factory :group do 
    id  1 
    attr_1 "default" 
    attr_2 "default" 
    ... 
    attr_n "default" 
    initialize_with { Group.find_or_create_by_id(id)} 
    end 

    factory :user do 
    association :group 
    end 
end 

para los carriles 4

La forma correcta en los carriles 4 es Group.find_or_create_by(name: name) , entonces usaría

initialize_with { Group.find_or_create_by(name: name) } 

en su lugar.

+6

Funciona muy bien, gracias. En Rails 4, la forma preferida sería: 'Group.find_or_create_by (name: name)' – migu

+19

La forma preferida en Rails 4 sería en realidad 'Group.where (name: name) .first_or_create'. – Laurens

+0

Probablemente esta sea la respuesta aceptada. Drive-by-ers ... aquí está su solución. – ocodo

4

También puede utilizar una estrategia para lograr este factorygirl

module FactoryGirl 
    module Strategy 
    class Find 
     def association(runner) 
     runner.run 
     end 

     def result(evaluation) 
     build_class(evaluation).where(get_overrides(evaluation)).first 
     end 

     private 

     def build_class(evaluation) 
     evaluation.instance_variable_get(:@attribute_assigner).instance_variable_get(:@build_class) 
     end 

     def get_overrides(evaluation = nil) 
     return @overrides unless @overrides.nil? 
     evaluation.instance_variable_get(:@attribute_assigner).instance_variable_get(:@evaluator).instance_variable_get(:@overrides).clone 
     end 
    end 

    class FindOrCreate 
     def initialize 
     @strategy = FactoryGirl.strategy_by_name(:find).new 
     end 

     delegate :association, to: :@strategy 

     def result(evaluation) 
     found_object = @strategy.result(evaluation) 

     if found_object.nil? 
      @strategy = FactoryGirl.strategy_by_name(:create).new 
      @strategy.result(evaluation) 
     else 
      found_object 
     end 
     end 
    end 
    end 

    register_strategy(:find, Strategy::Find) 
    register_strategy(:find_or_create, Strategy::FindOrCreate) 
end 

Puede utilizar this gist. y luego hacer lo siguiente

FactoryGirl.define do 
    factory :group do 
    name "name" 
    end 

    factory :user do 
    association :group, factory: :group, strategy: :find_or_create, name: "name" 
    end 
end 

Esto es trabajo para mí, sin embargo.

Cuestiones relacionadas