Find or create a record through the factory_girl association

I have a User model that belongs to a group. The group must have a unique name attribute. The factory user and factory group are defined as:

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

When you create the first user, a new group is also created. When I try to create a second user, it fails because he wants to create the same group again.

Can I specify a factory_girl association method to search for the first existing record?

Note. I tried to define a method for handling this, but then I cannot use f.association. I would like to be able to use it in Cucumber scripts as follows:

 Given the following user exists: | Email | Group | | test@email.com | Name: mygroup | 

and this can only work if the association is used in the factory definition.

+60
ruby ruby-on-rails-3 cucumber factory-bot
Aug 22 '11 at 9:10
source share
5 answers

I ended up using a combination of methods found around the network, one of which was inherited factories proposed by duckyfuzz in another answer.

I have done the following:

 # 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 
+17
Aug 27 '11 at 8:02
source share

You can use initialize_with with find_or_create method

 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 

It can also be used with 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 

For Rails 4

The correct way in Rails 4 is Group.find_or_create_by(name: name) , so you should use

 initialize_with { Group.find_or_create_by(name: name) } 

instead.

+103
Aug 03 '12 at 16:27
source share

You can also use FactoryGirl's strategy to achieve this.

 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 

You can use this meaning . And then do the following

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

This works for me, however.

+6
Jan 05 '15 at 10:52
source share

I usually just make a few factory definitions. One for a user with a group and one for a groupless user:

 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 

THEN you can use them in your step definitions to create users and groups separately and combine them as desired. For example, you can create one grouped user and one single user and join a single user to a group of grouped users.

In any case, you should take a look at the socket gem, which will allow you to write steps such as:

 Given a user exists with email: "hello@email.com" And a group exists with name: "default" And the user: "hello@gmail.com" has joined that group When somethings happens.... 
+2
Aug 24 2018-11-11T00:
source share

I use exactly the scenario that you described in your question:

 Given the following user exists: | Email | Group | | test@email.com | Name: mygroup | 

You can expand it like:

 Given the following user exists: | Email | Group | | test@email.com | Name: mygroup | | foo@email.com | Name: mygroup | | bar@email.com | Name: mygroup | 

This will create 3 users with the group "mygroup". Since it is used in the same way as it uses the find_or_create_by function, the first call creates a group, the next two calls find the already created group.

0
Feb 17 '12 at 10:44
source share



All Articles