こんにちは、watanabeです。 RSpecの学習ブログの第二回です。
今回はファクトリ編です。minitestでいうところのフィクスチャ、テスト用のデータを作る部分になります。
ファクトリの導入
factory_bot_rails
をGemfileに追加して$ bundle install
すれば使えるようになります。
データを作る
$ bin/rails g factory_bot:xxx yyy
を実行するとファクトリがgenerateされます。
xxxはモデル等のデータの種類、yyyはモデルであればモデル名(user など)を入れる形です。
everydayrails-rspec-jp-2024 nabeyu$ bin/rails g factory_bot:model user create spec/factories/users.rb
factories/users.rbを覗いてみます。
FactoryBot.define do factory :user do end end
初期状態ではこのようにデータは空の状態です。
モデルに対してほしい属性を 属性名 { データの内容 }
と記述していくとファクトリを作ることができます。
FactoryBot.define do factory :user do first_name { "Aaron" } last_name { "Sumner" } email { "tester@example.com" } password { "dottle-nouveau-pavilion-tights-furze" } end end
これで準備完了です。
テスト内で FactoryBot.create(:user)
で新しいuserオブジェクトが生成されてテストDBに永続化されます。また、 FactoryBot.build(:user)
と書くと新しいオブジェクトが生成されてメモリ内に保存されます。
フィクスチャとファクトリの比較
フィクスチャは - yamlで記述する - 比較的速い - 複雑なフィクスチャはメンテナンスコストが高い - フィクスチャはデータをDBに読み込ませる際にActive Recordを介さない(つまり、バリデーション等の機能しない)
フィクスチャは本番環境のデータとの不整合が起こる可能性があるため、その点だけをとってもファクトリのメリットは大きいです。
必ずしもファクトリを使う必要はない
ファクトリは便利な機能ですが、別ファイルにオブジェクトの生成内容を記述するという性質上、そのオブジェクトの内容はテストそのものを見てもわからない場合があります。これではテストはDRYになりますが、可読性は下がってしまいますのでテスト内でオブジェクトを生成する処理を書くことも選択肢として残していくことが必要です。 この作るRubyオブジェクトを plain old Ruby objects(PORO)と言うそうです。
シーケンスでユニークなデータを作る
複数のユーザーが必要なシーンは多そうですが、その場合はどうしたらいいでしょうか?
モデルに一意のバリデーションが設定されている場合、ユーザーを2つ以上create
するとエラーが発生します。バリデーションのある属性はユニークにして生成しなければなりません。
複数のオブジェクトをユニークに作成するには「シーケンス」を使います。
FactoryBot.define do factory :user do first_name { "Aaron" } last_name { "Sumner" } sequence(:email) { |n| "tester#{n}@example.com" } password { "dottle-nouveau-pavilion-tights-furze" } end end
seequence(属性名) { ブロック }
でシーケンスを 定義してブロック内の返り値をファクトリとして生成します。このときにブロック変数が生成するたびに+1される数値になっていて、ブロック内でブロック変数を使って返り値を定義することでユニークな値を取得できるようになります。
関連を表現する
関連の表現はassociation
で可能です。
インスタンスの生成において依存する要素があるときに、関連する要素を作りながらオブジェクトを生成する機能です。
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
ownerとはuserのaliasです。
projectはuserと一対多の関係になるオブジェクトです。
生成する際にassociation
と入っていることでオブジェクトの生成時点で、関連する要素の作製も可能です。
教材はこちら
RSpec初心者の教材としては定番?の「Everyday Rails - RSpecによるRailsテスト入門」を使っています。 フィヨルドブートキャンプのメンター兼ソニックガーデンのエンジニアをされている伊藤淳一さんが訳をされています。 Rails7に対応修正した初心者〜中級者向けの内容となっているので信頼度が高いかなと思います。