Найти или создать запись через factory girl association


у меня есть модель пользователя, который принадлежит группе. Группа должна иметь уникальный атрибут name. Фабрика пользователей и фабрика групп определяются как:

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

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

при создании первого пользователя также создается новая группа. Когда я пытаюсь создать второго пользователя, он терпит неудачу, потому что он хочет снова создать ту же группу.

есть ли способ сказать factory_girl метод ассоциации, чтобы сначала искать существующую запись?

Примечание: я пытался определить метод для обработки этого, но тогда я не могу использовать F.association. Я хотел бы иметь возможность использовать его в сценариях огурца, как это:

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

и это может работать только в том случае, если ассоциация используется в Заводском определении.

5 55

5 ответов:

можно использовать initialize_with С find_or_create метод

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

он также может быть использован с кодом

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

Для Рельсов 4

правильный путь в рельсах 4 Group.find_or_create_by(name: name), так что вы могли бы использовать

initialize_with { Group.find_or_create_by(name: name) } 
.

Я в конечном итоге использовал смесь методов, найденных в сети, один из которых был унаследован фабриками, как это было предложено duckyfuzz в другом ответе.

Я сделал следующее:

# 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

вы также можете использовать стратегию 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

можно использовать в этом суть. А затем сделайте следующее

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

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

это работает для меня.

обычно я просто делаю несколько заводских определений. Один для пользователя с группой и один для пользователя без группы:

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

затем вы можете использовать их в своих определениях шагов для создания пользователей и групп отдельно и объединить их вместе по желанию. Например, можно создать одного сгруппированного пользователя и одного одинокого пользователя и присоединить его к группе сгруппированных пользователей.

в любом случае, вы должны взглянуть на огурец что позволит вам написать шаги например:

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....

Я использую именно тот сценарий огурца, который вы описали в своем вопросе:

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

вы можете расширить его, как:

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

это создаст 3 пользователей с группой "mygroup". Поскольку он используется так, как это использует функциональность "find_or_create_by", первый вызов создает группу, следующие два вызова находят уже созданную группу.