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: false
とunique: 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を使っています。