LoginSignup
8
9

More than 3 years have passed since last update.

【Rails、RSpec】Ancestryを用いている時のFactoryBotの作り方

Last updated at Posted at 2020-04-25

はじめに

この記事の続きです。
https://qiita.com/ratovia/items/c9dbb00e4f663a7a7ef9

ancestryカラムを持ったFactoryの作り方

Factoryの基本的な構文はこれです。

FactoryBot.define do
  factory :user do
    first_name { "John" }
    last_name  { "Doe" }
    admin { false }
  end
end

例えば、itemテーブル(商品)を作成するとしたら

  • まず、前回の記事の部分まで作ります。
factories/items.rb
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    user
    after(:build) do |item|
      item.images.attach(io: File.open('spec/fixtures/test_image.jpg'), filename: 'test_image.jpg', content_type: 'image/jpg')
    end
  end
end
  • 次に、カテゴリーのfactorybotを作成します。
factories/categories.rb
# not work
FactoryBot.define do
  factory :category do
    name {"レモン"}
    ancestry {"???"}
  end
end

この???の部分が困りますよね?
ancestryのgemに従うと、
ancestry {"1/2"}とかancestry {1}とか悩むと思います。
ここはancestryに自動で生成してもらいましょう!
一旦、nilでいいと思います。

factories/categories.rb
# this work
FactoryBot.define do
  factory :category do
    name {"レモン"}
    ancestry {nil}
  end
end

categoryとのアソシエーションの仕方

このように、after(:build)のなかで
親の生成、子供の生成、紐づけるカテゴリーの生成をやってみるのが良さそうです。

factories/items.rb
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    user
    after(:build) do |item|
      # 親、子、孫を作成して、孫をitemのcategory_idに紐付ける
      parent_category = create(:category)
      child_category = parent_category.child.create(name: "hello")      
      grand_child_category = child_category.child.create(name: "world")

      item.category_id = grand_child_category.id

      item.images.attach(io: File.open('spec/fixtures/test_image.jpg'), filename: 'test_image.jpg', content_type: 'image/jpg')
    end
  end
end

いい感じですね!
ただし、このような記述だとchild_category = parent_category.child.create(name: "hello")のように、
FactoryBotではない方法で、インスタンスを作っています。
このままでも良いですが、これを改善するには、FactoryBotをもっと効率よく作っていかないといけません。

factoryの入れ子(継承)にして、child_categoryとgrand_categoryを定義していきます。
factoryの入れ子(継承)については、以下一次ソースのInheritanceの項が参考になると思います。
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md

|f|のように変数定義すると、作成しようとするfactory自体を引数に取ることができ、
f.parentのように、ancestryのメソッドを用いて、親を定義しましょう!

factories/categories.rb
# this work
FactoryBot.define do
  factory :category do
    name {"レモン"}
    ancestry {nil}

    factory :child_category do |f|
      f.parent create(:category)

      factory :grand_category do |g|
        g.parent create(:child_category)
      end

    end 

  end
end

※以下2つは同義です。

factory :category
  factory :child_category do |g|
    g.name ~~~~
  end
end
factory :category do
  trait :child_category do
    after(:build) do |c|
      c.name ~~~~
    end
  end
end

itemのfactoryの方は、孫だけ生成するだけでいけそうです。

factories/items.rb
# this work
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    user
    after(:build) do |item|
      # create(:grand_child_category)のfactoryの中で、親の定義、さらに親の定義が始まる。
      item.category_id = create(:grand_child_category).id

      item.images.attach(io: File.open('spec/fixtures/test_image.jpg'), filename: 'test_image.jpg', content_type: 'image/jpg')
    end
  end
end

parent_idを用いた多階層モデルの場合

一般に、多階層の場合は、ancestryを使用すると思いますが、parent_idを使った場合も記載しておきます。

ニッチなので、結論のみ

spec/factories/items.rb
FactoryBot.define do
  factory :item do
    name { "アイテム" }

    after(:build) do |item|
      parent = create(:category, name: "親", parent_id: nil)
      child = create(:category, name: "子", parent_id: parent.id)
      grand_child = create(:category, name: "孫", parent_id: child.id)
      item.category_id = grand_child.id
      item.images.attach(io: File.open('spec/fixtures/test_image.jpg'), filename: 'test_image.jpg', content_type: 'image/jpg')
    end
  end
end
spec/factories/categories.rb
FactoryBot.define do
  factory :category do
    name { nil }
    parent_id { nil }
  end
end

rails db:seed RAILS_ENV=testとかで用意したカテゴリーでテストする場合

自分には難しいんで、後日追記!

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9