LoginSignup
0
0

FactoryBotでhas_oneの関連付けを表現する。traitの基本的な使い方。

Last updated at Posted at 2024-04-09

FactoryBotでhas_oneの関連付けを表現するのってどうやるんだっけ?ってなったので自分用に備忘録を残しました。

traitという機能を使用しました。
今回はtraitの基本的な使い方➕作業の全体像を記しています。

参考にした記事

前提条件

CustomerがProfileをhas_oneしています。

customer.rb
class Customer < ApplicationRecord
  has_one :profile, dependent: :destroy
end

profile.rb
class Profile < ApplicationRecord
  belongs_to :customer
end



本題のコード

customerの方のFactoryBot

spec/factories/customers.rb
FactoryBot.define do
  factory :customer do #各テストからcreate(:customer)で生成できるようになる

    trait :profile do #traitで条件を付与したものを生成できるようになる。create(:customer, :profile)の形で生成できる
      after(:build) do |customer|
        customer.build_profile
      end
    end
  end
end

after(:build)は、オブジェクトがメモリ上にビルドされた直後に、特定のアクションを実行するためのコールバックです。

つまり、この場合はブロック変数のcustomerがbuildされたもので、それがデータベースに保存される前にcustomer.build_profileをして、customerインスタンスに新しくprofileを紐づけています。


profileの方のFactoryBot

spec/factories/profile.rb
FactoryBot.define do
  factory :profile do
    customer
  end
end

上記については
だいぶ省略されていて、

factory :profile do
  association :customer
end

このassociationメソッドが省略されています。
これによってProfileモデルがCustomerモデルに対してbelongs_toの関係を持つことをしめされています。

テスト上での使い方

とてもシンプルな例。
自分で定義した「profile!」というインスタンスメソッドの挙動をテストしています。
CustomerがProfileを持っていないか確認してからprofileを作成するという内容のものです。
テストしたいインスタンスメソッドは以下の通り

customer.rb
class Customer < ApplicationRecord
  has_one :profile, dependent: :destroy
end

def profile!
  return if profile?
  create_profile!
end

実際のテスト

customer_spec.rb
require 'rails_helper'

RSpec.describe Customer do

describe 'profile!' do
    it 'profileを追加できる' do
      customer = create(:customer) #profileを持っていないので、profile!を実行したら追加できるはず
      expect { customer.profile! }.to change(Profile, :count).by(1) #count自体が増えることでそれを証明している
    end

    it 'customerが既にProfileを持っている場合は追加できない' do
      customer = create(:customer, :profile) #先ほどのtraitを使用して、既にprofileを持ったcustomerを生成している。
      target_id = customer.profile.id #has_oneのものは新しく生成されると更新されてしまうので、現在のprofileのidを比較するために変数に格納しておく
      customer.profile!
      expect(Profile.exists?(id: target_id)).to be true #既にprofileがあるなら生成されずに同じidのインスタンスを保持しているはず
    end
  end
end

has_oneの挙動についても理解が浅く実装に少し苦戦しました。
次回はこの記事読んでささっと実装できたら良いな〜と思っています。

間違い、こうするともっと良いなどがあったらぜひご指摘していただけると幸いです。

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