FactoryBot関連付け注意点、余計なデータが作成されてしまう仕組み
概要
関連付け、複数のサンプルデータの定義の仕方 + 呼び出し方
前提条件
・gemインストール済み.
・FactoryBotサンプルデータの定義の仕方、呼び出し方がわかる.
・↓このブログの内容が理解できる(5分ほどで読めます.)
FactoryBotの関連付け(注意点)
なお関連付けは以下の通りとする.
- 余計なデータを作成してしまう例
FactoryBot定義
spec/factories/ ├── notes.rb ├── projects.rb └── users.rb #user.rb FactoryBot.define do factory :user, aliases: [:owner] do first_name "Aaron" last_name "Sumner" sequence(:email) { |n| "tester#{n}@example.com" } password "dottle-nouveau-pavilion-tights-furze" end end #project.rb FactoryBot.define do factory :project do sequence(:name) { |n| "Project #{n}" } description "A test project." due_on 1.week.from_now association :owner end end #note.rb FactoryBot.define do factory :note do message "My important note." #FactoryBot.create(:project)と等価 association :project #FactoryBot.create(:user)と等価 association :user end end
noteに関するテスト
require 'rails_helper' RSpec.describe Note, type: :model do it 'noteのサンプルデータをFactoryBotを使用して呼び出してみる' do note = FactoryBot.create(:note) #関連付けを使用してオブジェクトを呼び出してみる puts "#{note.project.user_id}" puts "#{note.user_id}" end end
上のテストで期待することはどちらも同じuser_idが返ってくることです.
$ bin/rspec Note 1 2
テスト結果からわかることは、userが2人分作成されていることです. なぜ2人作成されてしまうのでしょうか? コードを順に見ていきましょう.
_____________________________________________________________________ ① require 'rails_helper' RSpec.describe Note, type: :model do it 'noteのサンプルデータをFactoryBotを使用して呼び出してみる' do note = FactoryBot.create(:note) #関連付けを使用してオブジェクトを呼び出してみる puts "#{note.project.user_id}" puts "#{note.user_id}" end end _____________________________________________________________________ ②,③ #note.rb FactoryBot.define do factory :note do message "My important note." #FactoryBot.create(:project)と等価 association :project #FactoryBot.create(:user)と等価 association :user end end ____________________________________________________________________ ④,⑤ #project.rb FactoryBot.define do factory :project do sequence(:name) { |n| "Project #{n}" } description "A test project." due_on 1.week.from_now association :owner end end
① FactoryBot.create(:note)が実行されると、spec/factories/notes.rbの内容が実行されます.
② message "My important note"が属性値として、代入されます.
③ association :projectでspec/factories/projectsが呼び出されます.つまりFactoryBot.create(:project)が実行されます.
④ name,description,due_onそれぞれカラムの値が設定されます.
⑤ association :ownerが実行されると、spec/factories/usersが呼び出されます.
つまりFactoryBot.create(:users)が実行され、一人目のuserが作成されました.(コードは省略します.)
※association :userではなく、association :ownerなのはモデルでuser_idではなく、owner_idになるように設定しているからです.
ここは今回の内容と関係ないので、association: ownerはassociation: userと同じことをしているという理解で大丈夫です.
下のコードが設定をしているコードです.
class Project < ApplicationRecord belongs_to :owner, class_name: 'User', foreign_key: :user_id end
すこし脱線しましたが、②のコードをもう一度見てみましょう.
association :userが記述されています.
このコードが実行されると、userが当たらに作成されますよね.
しかしすでに、⑤によってuserは作成されています.そうです!これが2人のuserが作られてしまう仕組みです.
2人のユーザが作られないようにするには,したコードのようにすれば防げます.
#note.rb FactoryBot.define do factory :note do message "My important note." #FactoryBot.create(:project)と等価 association :project # 上のassociation :projectで作成されたユーザを渡してあげる user: { project.owner } end end
テスト実行
$ bin/rspec Note 1 1
期待通りになりました.
まとめ
FactoryBotを使用すると、気づかないうちに余計なデータが作成されてしまう.
余計なデータが作成されてしまうとテストに時間がかかるため、適宜チェックしよう.