あきろぐ

いろいろめもするよ🐈🐈🐈

FactoryBotでインスタンスを一気に作成するときに値をユニークにしたい

create_listは、複数のインスタンスを作成するときに便利なメソッドですが、作成されるインスタンスのテストデータは同じになってしまいます。

create_list(:article, 3, title: 'test', description: 'test')

createメソッドで愚直に1つずつインスタンスを作成すれば、以下のように渡す引数をインスタンスごとに変えることはできますが、

create(:article, title: 'test1', description: 'test')
create(:article, title: 'test2', description: 'test')
create(:article, title: 'test3', description: 'test')

でもそれは面倒なのでもっと簡潔に書きたい、できれば一行で...という場合もあると思います。 そこで、一行で書ける解決方法について模索したのでまとめてみます。

1. 配列をループさせてインスタンス作成するときに渡す引数を変える

タイトルをユニークな値にしたいので、ユニークな値を持った配列をループさせてcreateメソッドでインスタンスを作成します。

['test1', 'test2', 'test3'].each { |title| create(:article, title: title, description: 'test') }

2. 先にインスタンスを作成し、後からインスタンスの持つ値を変更する

create_listメソッドで一気にインスタンスを作成し、後からタイトルを変更するパターンです。注意すべきなのは、create_listメソッドで初期データはDBに保存されますが、後で書き換えられたタイトルは保存されていないので、再度saveする必要があります。

create_list(:article, 3, title: 'test', description: 'test') { |v, i| v.title = 'test' + i.to_s }.map(&:save!)

3. そもそもFactoryBotの定義でユニークな値になるようにすればいい

そもそも論かもしれませんが、最初からsequenceを使ってtitleがユニークになるようにしとけばOKかもですね。

FactoryBot.define do
  factory :article do
    sequence(:title) { |n| "test#{n}" }
    description { 'test' }
  end
end

create_list(:article, 3)

github.com

sequenceを使ってtitleがユニークになるように定義していても、テストケースによってはデフォルト値から変更したい場合も発生すると思うので、上記の2パターンが有効な場合もありそうです。

参考

github.com