Kaigi on Rails 2020での学びや、感想をつらつらと書いていく。
今回は、メドピア(MedPeer)のtoshimaruさんによる、FactoryBotの正しい使い方について。
動画はこちら。
なぜFixtureが重要なのか?
- テストの前提が間違えばその後のテストも破綻するから
- テストごとに何度も繰り返し呼ばれるので、非効率な書き方をするとパフォーマンスが落ちる
FactoryBotを使うときに意識すべきこと
- Factoryのデフォルト定義はミニマルなデータセットになっているか?
- 各々のテストケースで使っているFactoryは最低限の前提状態をつくりだせているか?
RailsでFactoryBotを使う
# Railsの場合 group :test do gem "factory_bot_rails" end
FactoryBot.create
の FactoryBot
を省略する設定
# rails_helper.rb RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end # FactoryBot.create(:user) create(:user)
巨大なデフォルトデータになっている
FactoryBot.define do factory :user do first_name { "John" } last_name { "Doe" } after(:create) do |user| create_association1 create_association2 create_association3 end end end
- 巨大なデフォルトデータになっている
- これを利用するすべてのテストケースで最小限の前提状態の生成になっていない
- 無駄なデータ生成になっている
- 間違った前提状態になる
- パフォーマンス悪化につながる
最小限のデータセット定義をデフォルトFactoryとして定義するのがベストプラクティス。
spec/factories/~.rb
user = build(:user) user = create(:user) attrs = attributes_for(:user) stub = build_stubbed(:user)
buildで済むのにcreateを使っている
build
でいいところをあえて create
にしてしまっている。
user = build(:user) user = create(:user)
- create をすると、DBに保存された状態を作り出す
- build をすると、インスタンスを生成するだけで save はしない
- 必ずしも create にする必要はない
- バリデーションテストの場合、インスタンスさえできていればよい
- SQLが実行されるぶん、パフォーマンスが悪化される
- 塵も積もればテスト全体が遅くなる
ちなみに、これは「Everyday Rails」にも掲載されていたので自分も覚えていた。
なお、build だと created_at が生成されないので、これが作成日時が必要な場合はbuild_stubbed
を使えばよい。
user = build_stubbed(:user)
生成したデータの配列を返す build_list, create_list
正しい方法
built_users = build_list(:users, 25) created_users = create_list(:users, 25)
間違った方法
factory :user do factory :user_with_posts do after(:create) do |user| create_list(:post, 10, user: user) end end end
- 不必要に create_list して無駄なデータ生成をおこなっている
- 本当に必要な数だけ指定してレコード生成するようにする
知らなかったこと
知らないことがたくさんあったため、後で調べるようにメモしておく。
attrs = attributes_for(:user) stub = build_stubbed(:user)
FactoryBotの使い方ではないけど、新規ユーザーかどうかをチェックするメソッドも参考になった。(どんな期間で新規と定義するかはそのサービスの運営方針によるだろうけど)
class User < ActiveRecord::Base def new? create_at > 1.day.ago end end
FactoryBotが提供するLintツールがある。
FactoryBot.lint
定義されたFactoryを一通りcreateし、invalidなFactory定義を検知する。rakeタスク化して他のRubocop等のLintツールと同様にCIタスクのひとつとして実装しておくとよい。
感想
- 正しく定義ができている部分もあって少しだけ自信になった
- traitをあまり使えてないことに気づいたので、まず復習しよう
- FactoryBotの使い方で迷ったときにまた見たい
- 自分が知らないことを知れるので、こういったカンファレンスを視聴するのは大事だと思った。自分の引き出しが増える(しかも無料で)