【Rspec】build,create / sequence, trait, transientなど
build, createなどの違い
DBへ保存するか否かなど違いがある。
メソッド | 戻り値 | DB保存 | DB保存(アソシエーション) | ID |
---|---|---|---|---|
build() | モデルインスタンス | x | o | nil |
build_stubbed() | モデルインスタンス | x | x | 適当な値 |
create() | モデルインスタンス | o | o | DB保存された値 |
attributes_for() | パラメータハッシュ | x | x | なし |
sequence(連番)
sequenceで連番を作成できる。
(spec/factories/foods.rb) FactoryBot.define do factory :food do sequence(:name) { |n| "name-#{n}" } calorie { 0 } sequence(:calorie_theory) { |n| "calorie_theory-#{n}" } labels { '' } end end
p build(:food) p build(:food) #<Food id: nil, name: "name-1", labels: "", calorie: 0, calorie_theory: "calorie_theory-1", created_at: nil, updated_at: nil> #<Food id: nil, name: "name-2", labels: "", calorie: 0, calorie_theory: "calorie_theory-2", created_at: nil, updated_at: nil>
trait
複数の属性をグループ化できる。 例えば、userモデルのfactoryデータが存在するとして、あるときは一般ユーザーのデータを、あるときは管理ユーザーのデータを生成したい、という時に便利。
(spec/factories/foods.rb) FactoryBot.define do factory :food do sequence(:name) { |n| "name-#{n}" } calorie { 0 } sequence(:calorie_theory) { |n| "calorie_theory-#{n}" } labels { '' } trait :rice do name { '白ごはん' } calorie { 0 } calorie_theory { '白い食べ物はカロリーが白紙に戻る。' } labels { [''] } end trait :ice_coffee do name { 'アイスコーヒー' } calorie { 0 } calorie_theory { 'コーヒーを抽出している時、実はゼロカロリーの成分だけが抽出されている。' } labels { [''] } end end end
transient
モデルの属性以外のデータを一緒に生成できる。 transientは、各attributeの値にも利用できるし、callbackでも利用できる。
transientは生成した後に参照することはできない。 生成時にパラメータとして渡し、組み立ててファクトリの動的な生成に使える。
FactoryBot(FactoryGirl)チートシート - Qiita
FactoryGirlのtransientとtraitを活用する - Qiita
食べ物のテーブルのFactory。食べ物のジャンルのテーブルと多対多の関係。
(spec/factories/food_genres.rb) FactoryBot.define do factory :food_genre do sequence(:genre_name) { |n| "genre_name-#{n}" } calorie { 0 } sequence(:calorie_theory) { |n| "calorie_theory-#{n}" } end end
食べ物のジャンルのFactory。食べ物のテーブルと多対多の関係。
(spec/factories/foods.rb) FactoryBot.define do factory :food do sequence(:name) { |n| "name-#{n}" } calorie { 0 } sequence(:calorie_theory) { |n| "calorie_theory-#{n}" } labels { '' } # 多対多 trait :with_food_genre do transient do sequence(:genre_name) { |n| "genre_name-#{n}" } sequence(:calorie_theory) { |n| "calorie_theory-#{n}" } # calorie { 0 } end # 中間テーブルを作成する定義を内部で統合 after(:build) do |food, evaluator| food.food_genres << build(:food_genre, genre_name: evaluator.genre_name, calorie_theory: evaluator.calorie_theory) end end end end
中間テーブル。なんも書いてない。
(spec/factories/food_food_genres.rb) FactoryBot.define do factory :food_food_genre do end end
これでテストファイルの中でデータを生成したところ、関連テーブルのデータも生成されていた。
(テストファイル内) let(:food) { create(:food, :with_food_genre) } [6] pry(#<RSpec::ExampleGroups::MealRecordDecorator::DrawingCalorieTheory::Food_2::FoodGenre>)> food => #<Food:0x00007fddf8e920b8 id: 4, name: "name-1", labels: "", calorie: 0, calorie_theory: nil, created_at: Fri, 30 Oct 2020 10:24:06 JST +09:00, updated_at: Fri, 30 Oct 2020 10:24:06 JST +09:00> # 多対多のテーブル [7] pry(#<RSpec::ExampleGroups::MealRecordDecorator::DrawingCalorieTheory::Food_2::FoodGenre>)> food.food_genres => [#<FoodGenre:0x00007fddf4fb7a28 id: 1, genre_name: "genre_name-1", calorie: 0, calorie_theory: "calorie_theory-1", created_at: Fri, 30 Oct 2020 10:24:06 JST +09:00, updated_at: Fri, 30 Oct 2020 10:24:06 JST +09:00>] # 中間テーブル [8] pry(#<RSpec::ExampleGroups::MealRecordDecorator::DrawingCalorieTheory::Food_2::FoodGenre>)> food.food_food_genres => [#<FoodFoodGenre:0x00007fddf8ed24b0 id: 1, food_id: 4, food_genre_id: 1, created_at: Fri, 30 Oct 2020 10:24:06 JST +09:00, updated_at: Fri, 30 Oct 2020 10:24:06 JST +09:00>] # 配列の一つ目を取り出す [9] pry(#<RSpec::ExampleGroups::MealRecordDecorator::DrawingCalorieTheory::Food_2::FoodGenre>)> food.food_genres[0] => #<FoodGenre:0x00007fddf4fb7a28 id: 1, genre_name: "genre_name-1", calorie: 0, calorie_theory: "calorie_theory-1", created_at: Fri, 30 Oct 2020 10:24:06 JST +09:00, updated_at: Fri, 30 Oct 2020 10:24:06 JST +09:00>