web-dev-qa-db-ja.com

factory_girl関連付けを介してレコードを検索または作成します

グループに属するユーザーモデルがあります。グループには一意の名前属性が必要です。ユーザーファクトリとグループファクトリは次のように定義されます。

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

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

最初のユーザーが作成されると、新しいグループも作成されます。 2番目のユーザーを作成しようとすると、同じグループを再度作成しようとするため失敗します。

Factory_girl関連付けメソッドに既存のレコードを最初に探すように指示する方法はありますか?

注:これを処理するメソッドを定義しようとしましたが、f.associationを使用できません。このようなCucumberシナリオで使用できるようにしたいと思います。

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

これは、ファクトリ定義で関連付けが使用されている場合にのみ機能します。

60

最終的には、ネット上で見つかった複数のメソッドを使用することになりました。そのうちの1つは、別の回答の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
16

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

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の場合

Rails 4の正しい方法はGroup.find_or_create_by(name: name)であるため、使用します

_initialize_with { Group.find_or_create_by(name: name) } 
_

代わりに。

103

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

this Gist を使用できます。そして、次のことを行います

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

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

しかし、これは私のために働いています。

6
Hiasinho

通常、複数のファクトリ定義を作成します。 1つはグループを持つユーザー用で、もう1つはグループレスユーザー用です。

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

次に、これらをステップ定義で使用して、ユーザーとグループを個別に作成し、自由に結合できます。たとえば、1人のグループ化されたユーザーと1人の孤立したユーザーを作成し、孤立したユーザーをグループ化されたユーザーチームに参加させることができます。

とにかく、 pickle gem を見てください。次のような手順を記述できます。

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....
2
David Tuite

最近、同様の問題に直面しましたが、ここで試しました。

FactoryBotのbuildおよびcreateが引き続き正常に動作するようにするには、次を実行して、createのロジックのみをオーバーライドする必要があります。

factory :user do
  association :group, factory: :group
  # ...
end

factory :group do
  to_create do |instance|
    instance.attributes = Group.find_or_create_by(name: instance.name).attributes
    instance.reload
  end

  name { "default" }
end

これにより、buildが「オブジェクトの構築/初期化」のデフォルトの動作を維持し、データベースの読み取りまたは書き込みを実行しないため、常に高速になります。常に新しいレコードを作成しようとするのではなく、createのロジックのみがオーバーライドされ、既存のレコードが存在する場合はそれをフェッチします。

記事 これを説明しました。

0
Joey Cheng

私はあなたの質問で説明したキュウリのシナリオを正確に使用しています:

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

次のように拡張できます。

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

これにより、グループ「mygroup」を持つ3人のユーザーが作成されます。このように使用すると、「find_or_create_by」機能が使用されるため、最初の呼び出しでグループが作成され、次の2つの呼び出しで作成済みのグループが検索されます。