FactoryBotのbuildとcreate
まいど、りゅういちです。
今日は勉強しているRSpecで使うFactoryBotの復習です。
buildメソッドとcreateメソッド
Everyday Rails - RSpecによるRailsテスト入門には以下のような説明がありました。
次のことを覚えてください。
FactoryBot.build を使うと新しいテストオブジェクトをメモリ内に 保存します。FactoryBot.create を使うとアプリケーションのテスト用データベースにオブジェ クトを永続化します。(P.55)
他にも。
可能な限り FactoryBot.create よりも FactoryBot.build を使ってください。こうすればテストデータベ ースにデータを追加する回数が減るので、パフォーマンス面のコストを削減できます。(P.68)
buildとcreatetの違いや使い分けは以下のようになります。
build
メモリ上にインスタンスが作られます。 'build'メソッドで呼び出されたときにインスタンスが生成され、テストを抜けるとインスタンスは消滅します(下図がわかりやすい)
コード例はこんな感じ。
spec/factories/tasks.rb
はこれ。
FactoryBot.define do factory :task do title { 'Task' } content { "content" } deadline { Random.rand(from..to) } end end
っで、spec/system/task_spec.rb
はこれ。
require 'rails_helper' RSpec.describe Task, type: :system do describe 'Task一覧' do context '正常系' do it "一覧ページでTaskが表示されること" do task = build(:task) #---① visit tasks_path expect(current_path).to tasks_path expect(page).to have_content task.title expect(page).to have_content task.content end end # 以下にテストコードが続く
①のtask = build(:task)
はit "is valid with all attributes" do〜〜end
内で定義されています。build
ではDBに保存されず、テストを抜けるとメモリ上からインスタンスが消滅することになるので、別のテスト(it "hgehoge"~)ではtask
の読み込みはできません。
DBに格納されていないことは、コンソール上でcreated_at
やupdated_at
がnil
になっていることから確認できます。
[1] pry(#<RSpec::ExampleGroups::Task::Task::Nested>)> task = build(:task) => #<Task:0x00007f9aaf093318 id: nil, title: "Task", status: "todo", deadline: Thu, 26 Sep 2019 00:00:00 JST +09:00, created_at: nil, updated_at: nil>
したがって、上記で挙げたようなDBアクセスが必要ないテストでは`create'ではなく'build'メソッドを使います。
というのも、課題でやるぐらいのテスト量では問題にならないかもしれませんが、実務ではテスト処理のパフォーマンスも求められるようなので、負荷を軽くする意識が必要みたいです。
create
DB上にインスタンスを作成します(インスタンスの永続化ってやつ)
したがって、DBにアクセスしてデータを取ってくる必要がある場合(データの個数、削除、カラムのユニークに対するテストなど)はcreate
を使います。
コード例はこんな感じ。
describe 'Task削除' do context '正常系' do it 'Taskが削除されること' do task = create(:task) #---② visit tasks_path click_link 'Destroy' page.driver.browser.switch_to.alert.accept expect(current_path).to eq tasks_path expect(current_path).to have_content('タスクが削除されました') expect(current_path).not_to have_content task.title end end end
②でtask = create(:task)
でDBにtask
が格納されています。
そのためtasks_path
にアクセスすると、task
に対応したclick_link 'Destroy'
が出現します。
create
ではなくbuild
でtask
を定義した場合には、click_link 'Destroy'
が出現しないため、ここでエラーが起こります。
コンソールで確認すると、DBに格納されていることが分かります。
[2]pry(#<RSpec::ExampleGroups::Task::Task::Nested>)> task = create(:task) => #<Task:0x00007f9ab6275620 id: 1, title: "Task", status: "todo", deadline: Tue, 27 Aug 2019 00:00:00 JST +09:00, created_at: Tue, 27 Oct 2020 08:14:50 JST +09:00, updated_at: Tue, 27 Oct 2020 08:14:50 JST +09:00>
参考文献
・FactoryBotにおけるcreateとbuildの違い
・Relish
まとめ
・RSpecでもコンソールが使って都度確認すると理解度がめっちゃアップしました
・let
とlet!
とbefore
の挙動の違い、「遅延評価とは?」もコンソールを使って確認してる感じで書かこうと思います。