【factory_girl】子のファクトリ生成時に親の属性をまとめて設定する


タイトル読んでも「ちょっと何言ってるか分かんない」感じだが、要するにhas_onehas_manyで親子関係を持つモデルがあったとして、子のファクトリ生成時に親側の属性も設定するという話(その逆もしかり)。

たとえばUserクラスとConfigクラスがあったとして、それぞれ次のような関連を持ってるとする。

📄spec/factories/config.rb
class User < ApplicationRecord
  has_one :config
end

class Config < ApplicationRecord
  belongs_to :user
end

各ユーザーが各々設定を持つというシチュエーションを想定している。で、この場合に親のファクトリをcreate(:config)のような感じで作るときに、まとめてUser側の属性も設定したいということ。

通常の書き方だと、Configのインスタンスを得るにはこんな感じになるはず。(たぶん)

config = create(:user. name: '...', email: '...',
  config: build(:config, attr1: '...', attr2: '...')).config

それを次のように書けるようにする。

config = create(:config, attr1: '...', attr2: '...',
  user: { name: '...', email: '...' })

「なんだ、ほとんど変わんねえじゃねーか」とお思いになるかもしれない。しかし前者は、Configのインスタンスが欲しいんだということがひと目で分からない。また、build :config してるのもいささか冗長だ。後者の方がテストの関心がConfigにあることが一目瞭然だし、タイプ量も少なくてすむ。細かいことだけど、こういうのって結構大事だと思うんだな。

ファクトリ定義

で、肝心のファクトリの定義だが、私はこんな感じに定義している。

FactoryGirl.define do
  factory :config do
    attr1 '...'
    attr2 '...'

    transient do
      user { {} }
    end

    after(:build) do |config, evaluator|
      config.user = build(:user, evaluator.user) if evaluator.user.present?
    end
  end
end

usertransientにしておきuserの属性(Hash)を渡してもらう。after(:build)の中でuserのファクトリに横流してbuildしているのでuserのファクトリ定義を上書きすることができるというわけ。なお親から子の属性を設定する場合も同様の方法が使えると思う。

これがベストの解かは分からない。もっといい方法があれば教えて欲しいッス。

関連する記事


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください