ryota21silvaの技術ブログ

Funna(ふんな)の技術ブログ

これまで学んだ技術の備忘録。未来の自分が救われることを信じて

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