FactoryBotのcreate_listの内部実装から紐解くテストデータ生成戦略

>100 Views

October 28, 25

スライド概要

FactoryBotのcreate_listメソッドについて、どのように同じファクトリのレコードをまとめて作成できるのか、またその実装にはストラテジーパターンが使われていることを解説します。しかし、create_listメソッドを使った場合、パフォーマンスに影響が出る可能性があるため、insert_allメソッドやactiverecord-import gemのimportメソッドといった代替案を提案し、それぞれのパフォーマンスを比較します。最終的に、どのメソッドを選ぶべきかの指針を示します。

profile-image

山口県からフルリモートで働いているソフトウェアエンジニア

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

FactoryBotのcreate_listの内部実装から紐解く テストデータ⽣成戦略 HRT

2.

⾃⼰紹介 HRT(@hrt_sc) ツクリンク株式会社 ソフトウェアエンジニア - SaaSやtoB向けマッチングサービスを運営する会社での開発を経て現職 趣味は、サッカー、旅⾏、アウトドア(キャンプ‧釣り)

3.

FactoryBotのcreate_listメソッド、使ってますか?

4.

FactoryBotのcreate_listメソッドとは? ✅ 同じファクトリのレコードをまとめ create(:user) て作成できる create(:user) ✅ ⼀⾏で書くことができ、可読性が向 ↓ 上する create(:user) create_list(:user, 3)

5.

create_listメソッドの実装について create_listメソッドの定義には「ストラテジーパターン」が使われてい る つまり、、、 「create_listというメソッド名と、createというストラテジーを紐付け て指定したファクトリを指定回数繰り返し⽣成して、配列を返す」 メソッドを定義している

6.

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

7.

考えられるデメリット

8.

大量件数のデータ作成によるパフォーマンスへの影響 create_listメソッドは内部的にcreateメソッドを指定回数繰り返し実⾏ する 結果として、INSERTが⼀件ずつ指定回数分発⽣するため件数が多いとパ フォーマンスに影響がある

9.

起こりうる弊害 開発⽣産性の低下 コストの増加 ローカルでの実⾏やCI実⾏時 間が伸びることによって、 開発者の待機時間が増え、 リードタイムが伸びる CI実⾏時間増加などに伴っ て、 CIツールのコストが増加する ことも考えられる

10.

create_listメソッドの代替案

11.

代替案① insert_all ✅ Rails6.0で追加された複数 user_attributes_list = レコード⼀括登録機能 100.times.map { attributes_for(:user) } User.insert_all(user_attributes_list) ✅ バルクインサートのため⾼速 ⚠ バリデーションとコールバック が実⾏されない

12.

代替案② import ✅ 「activerecord-import」 gem users = build_list(:user, 100) によるバルクインサート機能 User.import(users, validate: false) ✅ バリデーションの有無を引数で 指定可能

13.

パフォーマンス⽐較

14.

パフォーマンスの⽐較 Benchmarkを使って、下記4パターンで100件のレコード作成を100回 ループして⽐較 1. create_list 2. insert_all 3. import(validation: true) 4. import(validation: false)

15.

結果 メソッド 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秒

16.

まとめ 💡 insert_allが圧倒的に⾼速 メソッド 利⽤シーン create_list 件数が少ない、可読性重視 insert_all バリデーションやコールバックをスキップしても問題ない import 「activerecord-import」gemを導⼊可、バリデーションを⾏い たい場合

17.

ご清聴ありがとうございました

18.

参考資料 FactoryBotのcreate_listメソッドとパフォーマンスを考慮した代替 案について