学習記録

アウトプット用に作りました

FactoryBotについて

FactoryBotとは

テストデータ生成のためのデータライブラリのことです。
ここで作成したデータはテストケースで呼び出すことができます。


FactoryBotの使い方

GemFileにFactoryBotを追加します。
開発環境とテスト環境のみで使うものなので、下記のように指定してbundle installします。

group :development, :test do
  gem 'factory_bot_rails'
end

factoriesディレクトリの配下にUserモデルのデータファイルを生成します。

$ bin/rails g factory_bot:model user


まずusersテーブルのカラムを確認しました。

db/schema.rb

create_table "users", force: :cascade do |t|
    t.string "email", null: false
    t.string "crypted_password"
    t.string "salt"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
end

これを見るとemailカラムにはnull: falseunique: trueがあります。なのでemailは絶対必要なデータであり、同じEmailアドレスは重複して登録されてはいけません。

このことを踏まえてテストデータを作成していきます。

FactoryBot.define do
  factory :user do
    sequence(:email) { |n| "user_#{n}@example.com" }
    password { "password" }
    password_confirmation { "password" }
  end
end

unique制約のあるカラムは、sequenceを使って重複したデータが作成されないようにします。


補足ですがsequenceにはもう一つの書き方があります。

sequence(:name){|n| "name_#{n}"}
sequence(:name, "name_1")

sequence (:email, "user_1@example.com")とすると、末尾の数値や文字を増やしてしまうので、この場合"user_1@example.con"となってしまい、想定していた結果(数字を増やすこと)を取得できません。

参考 : FactoryBot (旧FactoryGirl) の sequence と .next - Qiita


早速テストケースで作成したFactoryBotを使ってみます。

user = FactoryBot.create(:user)

これでuserのデータを作成することができます。


FactoryBot.の省略方法

設定を追加することで、FactoryBot.の部分を省略することもできます。

spec/rails_helper.rb

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

この設定をすることで

user = create(:user)

FactoryBotの記述が省略できました。


モデルのアソシエーションを組んでいる場合

UserモデルとTaskモデルがあったとします。userはたくさんのtaskを持っている、という一対多の関係性があった時、taskのFactoryにアソシエーションを定義すると、テストでわざわざuserのテストデータを呼び出す必要がなくなります。

factories/tasks.rb

FactoryBot.define do
  factory :task do
    sequence(:title, "title_1")
    content { "content" }
    status { :todo }
    deadline { 1.week.from_now }
    association :user
  end
end

このように定義しておくと、

user = create(:user)
task = create(:task)

ではなく、

task = create(:task)

taskのデータを作成すれば、userのデータも一緒に作成されます。

参考 : FactoryBotのassociationとは - Qiita


createとbuildの違い

it 'is invalid with a duplicate title' do
    task = create(:task)
    task_with_duplicated_title = build(:task, title: task.title)
    expect(task_with_duplicated_title).to be_invalid
    expect(task_with_duplicated_title.errors[:title]).to eq ["has already been taken"]
end

ここでcreateとbuildをどうやって使い分けているのか疑問に思いました。

create テストデータをデータベース上に保存して、データを永続化させます。
build テストデータをメモリ上のみで記録します。

このテストケースではタイトルが重複しているのは無効というのを調べています。
まず最初のコードでtaskを作り、二番目のコードでtaskと同じタイトルを持つtask_with_duplicated_titleを作ります。
この場合taskのデータはtask_with_duplicated_titleと比較しなくてはいけないので持続的に保持しておく必要がありますよね。なのでここではcreateを使っています。

参考 : FactoryBotにおけるcreateとbuildの違い - Qiita