こんにちは!oda@エンジニア1年目です!
先日、FactoryBotでテストデータを作成する際にDBに保存されているデータを使いたかったのですが、方法がすぐにはわかりませんでした。
今回は、その方法について調べた内容を具体例とともにまとめました。
事前準備
まずはFoodモデルとOrderモデルを次のとおり定義します。
次にOrder
のインスタンス作成時に使用するマスタデータとして、Food
モデルに以下のデータをあらかじめ準備しておきます。
id | name | price |
---|---|---|
1 | 弁当A | 500 |
Order
のprice
には、基本的にfood_id
で指定したFood
のprice
を使います。
それでもorder.food.price
のように呼び出さず、わざわざOrder
にprice
カラムを設けている理由は、弁当Aはprice
を個別に400円や350円に値引きして設定したいときがあるというイメージです。
テストの作成
今回のテストにはRSpecを使います。作成したテストは次のとおりです。
spec/model/order_spec.rb
require 'rails_helper' RSpec.describe Order, type: :model do context 'create' do it '正しく注文が作成されていること' do food = Food.find(1) order = FactoryBot.create(:order, food_id: food.id) expect(order.food_id).to eq food.id expect(order.price).to eq food.price end end end
失敗するケース
まずはテストが失敗するケースから確認していきます。
FactoryBotを次のとおり作成します。
spec/factories/orders.rb
FactoryBot.define do factory :order do food_id { 1 } price { 100 } quantity { 1 } end end
テストを実行すると、結果は次のとおりです。
$ rspec spec/models/order_spec.rb F Failures: 1) Order create 正しく注文が作成されていること Failure/Error: expect(order.price).to eq food.price expected: 500 got: 100 (compared using ==) # ./spec/models/order_spec.rb:9:in `block (3 levels) in <top (required)>' Finished in 0.18945 seconds (files took 4.97 seconds to load) 1 example, 1 failure Failed examples: rspec ./spec/models/order_spec.rb:5 # Order create 正しく注文が作成されていること
order
の作成時に、設定しているのはfood_id
のみなので、当然price
はFactoryBotで指定している100のままです。
spec/model/order_spec.rbでorder = FactoryBot.create(:order, food_id: food.id, price: food.price)
とすることで上記を解決することはできます。
ただ、そのテストとしてprice
に意味がある値を設定する時以外はわざわざ書きたくありません。
このため、FactoryBot内でDBに保存されているデータを使ってprice
を設定したいと感じました。
FactoryBot内でDBのデータを呼び出す
調べているとFactoryBotでコールバックを使用する方法が見つかったので、こちらを試してみます。
参考:factory_bot/GETTING_STARTED.md
先ほどのFactoryBotを次のとおり修正します。
spec/factories/orders.rb
FactoryBot.define do factory :order do before(:create) do food = Food.find(1) end food_id { food.id } price { food.price } quantity { 1 } end end
これでFactoryBot.create
でテストデータが作成される前にbefore(:create)
が実行され、DBから呼び出したデータがFactoryBot内で使えるようになるはずです。
テストを実行すると、期待どおりうまくいきました!
$ rspec spec/models/order_spec.rb . Finished in 0.25619 seconds (files took 5.71 seconds to load) 1 example, 0 failures
さいごに
業務中は、時間的な制約もありこの方法を調べて使うことができませんでした。
今回の記事を書く中で、この方法が使えそうだということの確認ができたので、次の機会で活かせることが楽しみです!