LoginSignup
5
3

More than 5 years have passed since last update.

FactoryGirl.lint で valid なテストデータを作れているかチェックする

Last updated at Posted at 2017-04-08

この記事では、RSpec + factory_girl な環境で、factory の定義がモデルの validation に対して適切になっているかチェックする方法を紹介します。

例えば、あるモデルについて新しい factory を追加する場合に、定義が誤っていて、テストデータ自体が ActiveRecord モデルの validation を通らないことが起こりえます。このとき、そのテストデータを使っているテスト実行時に大量にエラーが出たりすると、原因の切り分けが面倒になるかもしれません。

ここで、factory_girl には lint 機能が用意されています。この機能を使うと、各 factory を実行して、データ作成時になにか例外が起こると FactoryGirl::InvalidFactoryError を投げてエラーを検出してくれます。これを RSpec のテスト実行前に一度だけ実行しておくと、factory 自体に問題があることがすぐにわかるようになります。

実行例

実際に lint でエラーが検出される例を示します。

次のモデルがあるとします。

app/models/user.rb
class User < ApplicationRecord
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true

  # ...
end

このモデルに対して、次の factory を定義したとします。ここでは、わざと同じ email しか生成しないようにしています。

spec/factories/users.rb
FactoryGirl.define do
  factory :user do
    name { FFaker::Name.name }
    email 'fixed@example.com'
  end
end

この後の「導入方法」で説明する方法を実行したあとに RSpec を実行すると、FactoryGirl.lint によって次のようなエラーが表示されます。

$ bundle exec rake
rake aborted!
FactoryGirl::InvalidFactoryError: The following factories are invalid:

* user - Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
# 以下スタックトレース

このようなエラーが出たならば、factory の定義がおかしいということになるので、そちらを修正すればよいということがすぐにわかります。

導入方法

この記事で利用するツールのバージョンは次のとおりです。

  • rspec-rails v3.5.2
  • factory_girl_rails v4.8.0

gem の追加

factory_girl は導入済みとします。別途、database_rewinder が導入済みでないなら導入します。

Gemfile
group :development, :test do
  # ...
  gem 'database_rewinder'
end

lint 実行用タスクの追加

次に、普通のテスト実行前に lint を実行する Rake タスクを作ります。

lib/tasks/spec_with_lint.rake
desc 'Run all specs in spec directory with factory linting (excluding plugin specs)'
task spec_with_lint: :environment do
  if Rails.env.test?
    begin
      FactoryGirl.lint trait: true
    ensure
      DatabaseRewinder.clean_all
    end
    Rake::Task[:spec].invoke
  else
    system "bundle exec rake spec_with_lint RAILS_ENV='test'"
  end
end

FactoryGirl.lint の実行時に traits: true を指定すると、factory だけではなく trait に関しても lint を実行できます。また、lint が成功したときとエラーを検出したときの両方で lint 時に作成したレコードを確実に削除しています。FactoryGirl.lint の実行が終わったあと、rspec-rails デフォルトの spec タスクをキックして、各テストを実行しています。

デフォルトタスク化

最後に、rake を引数なしで叩いたときに実行するデフォルトのタスクを上で定義した spec_with_lint にします。

ただし、FactoryGirl.lint は引数で factory を指定しない限り、すべての factory についてひととおりデータを作成します。このため、factory の数が増えると、一つの単体テストだけ実行したいときなどに速度の低下が無視できなくなってくると思います。そこで、lint なしのテストをするタスク spec を実行したいときは rake spec を叩けばよいようにします。

やることは Rakefile で Rails 用タスクをロードする前にデフォルトタスクを上書きするだけです。

Rakefile
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require_relative 'config/application'

task default: :spec_with_lint # 追加

Rails.application.load_tasks

これで、次のコマンドで lint ありテストを実行できます。

$ bundle exec rake

また、次のようなコマンドで lint なしテストを実行できます。

$ bundle exec rake spec spec/models/user_spec.rb:10 # イメージ

参考資料

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3