今回はRSpecのテストコードを効率化できるツール「FactoryBot」について初心者向けに解説したいと思います。
FactoryBotを用いずともテストコードを書くことは可能ですが、それではダラダラと長いコードになってしまいます。FactoryBotの使い方をマスターし、より洗練されたテストコードが書けるよう学習していきましょう。
FactoryBotとは
FactoryBotとは、サンプルデータ(≒ ダミーのインスタンス)を簡単に作成することができるテストツールです。「factory_bot_rails」というGemをインストールすることでFactoryBotが使えるようになります。
では、FactoryBotを使用することでどのくらいテストコードが効率化できるのか見ていきましょう。
例えば以下のコードでは、nicknameとemailの値が空である場合はユーザー登録ができないことをテストしています。
require 'rails_helper'
describe User do
context 'not be able to create' do
it "is invalid without a nickname" do
user = User.new(nickname: "", email: "aaa@gmail.com", password: "00000000", password_confirmation: "00000000")
user.valid?
expect(user.errors[:nickname]).to include("can't be blank")
end
end
end
describe User do
context 'not be able to create' do
it "is invalid without an email" do
user = User.new(nickname: "test", email: "", password: "00000000", password_confirmation: "00000000")
user.valid?
expect(user.errors[:email]).to include("can't be blank")
end
end
end
「user = User.new(~~~~)」を毎回の実行していることが分かると思います。
しかし、FactoryBotを使用することで「use = User.new(~~~~)」の記述を1箇所にまとめることができ、よりシンプルなコードにすることができます。言い換えれば、毎回インスタンスを生成する必要がなくなるということです。
ではFactoryBotを使用した場合の記述例を見ていきましょう。
# spec/factories/users.rb
FactoryBot.define do
factory :user do
nickname {"test"}
email {"test@gmail.com"}
password {"12345678"}
password_confirmation {"12345678"}
end
end
# spec/models/user_spec.rb
describe User do
context 'not be able to create' do
it "is invalid without a nickname" do
user = FactoryBot.build(:user, nickname: "")
user.valid?
expect(user.errors[:nickname]).to include("can't be blank")
end
it "is invalid without an email" do
user = FactoryBot.build(:user, email: "")
user.valid?
expect(user.errors[:email]).to include("can't be blank")
end
end
end
spec/factories/users.rbでユーザーのサンプルデータを作成しておき、実際のテストコードではFactoryで定義したデータの内、テストしたい部分だけを上書きしています。
具体的に説明すると、以下のコードではuserのFactoryBotを呼び出し、nicknameだけは空の状態で上書きしています。
user = FactoryBot.build(:user, nickname: "")
さらに言うと、以下の2つのコードは同じことを意味しています。
user = User.new(nickname: "test", email: "test@gmail.com", password: "12345678", password_confirmation: "12345678")
# spec/factories/users.rb
FactoryBot.define do
factory :user do
nickname {"test"}
email {"test@gmail.com"}
password {"12345678"}
password_confirmation {"12345678"}
end
end
# spec/models/user_spec.rb
user = FactoryBot.build(:user)
このように、FactoryBotを使用することで何度も使用するデータをあらかじめ作成しておくことができます。テストコードではそれらを呼び出すだけなので非常に便利ですね。
FactoryBotの導入
FactoryBotの使い方が分かったので、次はFactoryBotの導入について解説します。
まずはGemfileのdevelopment/test環境に「factory_bot_rails」を記述しましょう。記述後はbundle installを実行してください。
group :development, :test do
# 省略
gem 'rspec-rails'
gem 'factory_bot_rails'
end
% bundle install
続いてfactoriesを作成するため、以下のコマンドを打ち込みます。
そうすることでfactoriesディレクトリ直下にusers.rbが作成されます。
% rails g factory_bot:model user
作成されたusers.rbを確認すると以下のように雛形が出来上がっています。後はここにサンプルデータを記載するだけです。
FactoryBot.define do
factory :user do
end
end
よりコードをスッキリさせるには
ここまででも大分綺麗なコードにはなりました。
しかし以下のようにFactoryBotという表現を省略し、よりコードをすっきりさせることができます。
user = FactoryBot.build(:user, nickname: "")
↓
user = build(:user, nickname: "")
user = FactoryBot.build(:user, email: "")
↓
user = build(:user, email: "")
上記のようにFactoryBotという文言を省略するには、spec/rails_helper.rbを以下のように編集する必要があるので忘れずに追記するようにしましょう。
RSpec.configure do |config|
# 下記を追記
config.include FactoryBot::Syntax::Methods
#省略
end
Fixtureとの比較
Railsではサンプルデータを生成する手段として、Fixture(フィクスチャ)と呼ばれる機能がデフォルトで提供されています。FixtureはYAML形式のファイルです。
例えば、ユーザーのサンプルデータをFixtureで作成すると以下のようになります。
# users.yml
person:
name: "test"
email: "test@gmail.com"
password: "12345678"
password_confirmation: "12345678"
そしてそのサンプルデータを呼び出すには、テストコードで以下の記述をします。
users(:person)
FactoryBotを同じく、比較的簡単にサンプルデータを作成することができましたね。
ただFixtureには1つ大きな欠点があります。
それは、RailsはFixtureのデータを読み込む際にActiveRecordを使用しないということです。言い換えれば、モデルのバリデーションのような重要な機能が無視されてしまうということです。
そのため個人的にはFactoryBotの使用をおすすめします。
まとめ
- FactoryBotとは、サンプルデータを簡単に作成することができるテストツール
- FactoryBotを使用するにはfactory_bot_railsというGemをインストールする必要がある
- 似たような機能にFixtureがあるが、ActiveRecordを使用しないという欠点がある
参考
今回はRSpecのテストコードを効率化できるツール「FactoryBot」について初心者向けに解説しました。今後RSpecのサンプルデータを作成する際はFactoryBotを使用するよう心がけていきましょう。