2011-08-22 14 views
50

Mam model użytkownika należący do grupy. Grupa musi mieć unikalny atrybut nazwy. Fabryka fabryki i grupy użytkowników jest zdefiniowana jako:Znajdź lub utwórz rekord przez stowarzyszenie factory_girl

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

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

Po utworzeniu pierwszego użytkownika tworzona jest również nowa grupa. Kiedy próbuję utworzyć drugiego użytkownika, to się nie uda, ponieważ chce ponownie utworzyć tę samą grupę.

Czy istnieje sposób na wskazanie metody powiązania factory_girl, aby najpierw sprawdzić istniejący rekord?

Uwaga: Próbowałem zdefiniować metodę radzenia sobie z tym, ale nie mogę użyć f.association. Chciałbym, aby móc używać go w scenariuszach ogórka tak:

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

a to może działać tylko wtedy, gdy stowarzyszenie jest stosowany w definicji fabryki.

Odpowiedz

16

skończyło się używając kombinacji metod znaleźć wokół siatki, jedna z nich jest dziedziczonych fabrykach jak zaproponowane przez duckyfuzz w innej odpowiedzi.

zrobiłem następujący:

# 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

Zazwyczaj po prostu tworzę wiele definicji fabrycznych. Jeden dla użytkownika z grupy i jeden dla użytkownika groupless:

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 

wtedy można z nich korzystać w definicjach kroku do tworzenia użytkowników i grup oddzielnie i połączyć je ze sobą do woli. Na przykład możesz utworzyć jednego zgrupowanego użytkownika i jednego samotnego użytkownika i dołączyć do samotnego użytkownika do zespołu zgrupowanych użytkowników.

W każdym razie, należy spojrzeć na pickle gem który pozwoli Ci napisać czynności, takich jak:

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

używam dokładnie scenariusz Ogórek ty opisanej w pytaniu:

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

można rozszerzyć to lubią:

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

ten utworzy 3 użytkowników z grupą "mygroup". Jak to było używane, to wykorzystuje funkcję "find_or_create_by", pierwsze wywołanie tworzy grupę, kolejne dwa wywołania znajdują już utworzoną grupę.

89

Można używać initialize_with z find_or_create metody

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 

To może być również używany z 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 

dla szyn 4

Prawidłowy sposób w Rails 4 jest Group.find_or_create_by(name: name) , więc użyjesz

Zamiast tego 210
initialize_with { Group.find_or_create_by(name: name) } 

.

+6

Działa bardzo dobrze, dzięki. W Railsach 4 preferowanym sposobem byłoby: 'Group.find_or_create_by (name: name)' – migu

+19

Preferowanym sposobem w Railsach 4 byłaby w rzeczywistości 'Group.where (name: name) .first_or_create'. – Laurens

+0

To prawdopodobnie powinna być akceptowana odpowiedź. Drive-by-ers ... oto twoje rozwiązanie. – ocodo

4

Można również użyć strategię FactoryGirl aby osiągnąć ten

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 

Można użyć this gist. a następnie wykonaj następujące

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

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

To działa dla mnie, choć.

Powiązane problemy