>100 Views
October 28, 25
スライド概要
FactoryBotのcreate_listメソッドについて、どのように同じファクトリのレコードをまとめて作成できるのか、またその実装にはストラテジーパターンが使われていることを解説します。しかし、create_listメソッドを使った場合、パフォーマンスに影響が出る可能性があるため、insert_allメソッドやactiverecord-import gemのimportメソッドといった代替案を提案し、それぞれのパフォーマンスを比較します。最終的に、どのメソッドを選ぶべきかの指針を示します。
FactoryBotのcreate_listの内部実装から紐解く テストデータ⽣成戦略 HRT
⾃⼰紹介 HRT(@hrt_sc) ツクリンク株式会社 ソフトウェアエンジニア - SaaSやtoB向けマッチングサービスを運営する会社での開発を経て現職 趣味は、サッカー、旅⾏、アウトドア(キャンプ‧釣り)
FactoryBotのcreate_listメソッド、使ってますか?
FactoryBotのcreate_listメソッドとは? ✅ 同じファクトリのレコードをまとめ create(:user) て作成できる create(:user) ✅ ⼀⾏で書くことができ、可読性が向 ↓ 上する create(:user) create_list(:user, 3)
create_listメソッドの実装について create_listメソッドの定義には「ストラテジーパターン」が使われてい る つまり、、、 「create_listというメソッド名と、createというストラテジーを紐付け て指定したファクトリを指定回数繰り返し⽣成して、配列を返す」 メソッドを定義している
define_list_strategy_methodというメソッドが使われている def define_list_strategy_method strategy_name = @strategy_name define_syntax_method("#{strategy_name}_list") do |name, amount, *traits_and_overrides, &block| unless amount.respond_to?(:times) raise ArgumentError, "count missing for #{strategy_name}_list" end Array.new(amount) do |i| block_with_index = StrategySyntaxMethodRegistrar.with_index(block, i) send(strategy_name, name, *traits_and_overrides, &block_with_index) end end end 参考: https://github.com/thoughtbot/factory_bot/blob/v6.5.6/lib/factory_bot/strategy_syntax_method_registrar.rb
考えられるデメリット
大量件数のデータ作成によるパフォーマンスへの影響 create_listメソッドは内部的にcreateメソッドを指定回数繰り返し実⾏ する 結果として、INSERTが⼀件ずつ指定回数分発⽣するため件数が多いとパ フォーマンスに影響がある
起こりうる弊害 開発⽣産性の低下 コストの増加 ローカルでの実⾏やCI実⾏時 間が伸びることによって、 開発者の待機時間が増え、 リードタイムが伸びる CI実⾏時間増加などに伴っ て、 CIツールのコストが増加する ことも考えられる
create_listメソッドの代替案
代替案① insert_all ✅ Rails6.0で追加された複数 user_attributes_list = レコード⼀括登録機能 100.times.map { attributes_for(:user) } User.insert_all(user_attributes_list) ✅ バルクインサートのため⾼速 ⚠ バリデーションとコールバック が実⾏されない
代替案② import ✅ 「activerecord-import」 gem users = build_list(:user, 100) によるバルクインサート機能 User.import(users, validate: false) ✅ バリデーションの有無を引数で 指定可能
パフォーマンス⽐較
パフォーマンスの⽐較 Benchmarkを使って、下記4パターンで100件のレコード作成を100回 ループして⽐較 1. create_list 2. insert_all 3. import(validation: true) 4. import(validation: false)
結果 メソッド 1回の平均時間 (real) 合計時間 (real) create_list 0.766秒 76.57秒 insert_all 最速 0.010秒 1.04秒 import | validate: true 0.060秒 6.02秒 import | validate: false 0.040秒 3.96秒
まとめ 💡 insert_allが圧倒的に⾼速 メソッド 利⽤シーン create_list 件数が少ない、可読性重視 insert_all バリデーションやコールバックをスキップしても問題ない import 「activerecord-import」gemを導⼊可、バリデーションを⾏い たい場合
ご清聴ありがとうございました
参考資料 FactoryBotのcreate_listメソッドとパフォーマンスを考慮した代替 案について