Ruby
Rails
FactoryGirl

RSpecにおけるFactoryGirlまとめ

More than 1 year has passed since last update.

はじめに

FactoryGirlを使って簡単なモックオブジェクトは作れるが、associationやらtraitやらsequenceやら便利な機能をいちいち調べるのめんどくさいので、今のうちにまとめておく。

FactoryGirlインストール

Gemfile
group :test do
  gem 'rspec-rails'
  gem 'factory_girl_rails'
end

version指定しないと最新版がインストールされるので、
version指定する場合は、

Gemfile
group :test do
  gem 'rspec-rails'
  gem 'factory_girl_rails', '~> 4.0'
end

んで、 bundle installして完了

FactoryGirlのファイル生成

基本的にFactoryGrilをインストールした状態で、モデルを作成するとモデルと一緒にファクトリーも生成される。

$ rails g model User name:string

Running via Spring preloader in process 39201
      invoke  active_record
      create    db/migrate/20170720132710_create_users.rb
      create    app/models/user.rb
      invoke    rspec
      create      spec/models/user_spec.rb
      invoke      factory_girl
      create        spec/factories/users.rb

ちなみに、作成されたファクトリーの中身は、こんな感じ。

spec/factories/user.rb
FactoryGirl.define do
  factory :user do
    name "MyString"
  end
end

ここにテストデータを書いていきます。

FactoryGirlの定義

spec/factories/user.rb
FactoryGirl.define do
  factory :user do
    name "hoge"
  end
end

RSpec実行コマンド

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe "User" do
    context "user.nameが生成されるテスト" do
      it "user.nameがhogeである" do
        user_name = FactoryGirl.create(:user).name
        expect(user_name).to eq "hoge"
      end
    end
  end
end

FactoryGirlの設定

FactoryGirlを生成するときに、

FactoryGirl.create(:user)

と書きますが、FactoryGirlの部分は省略できます。

spec/rails_helper.rb
RSpec.configure do |config|
  ...
  config.include FactoryGirl::Syntax::Methods

  #config.fixture_path = "#{::Rails.root}/spec/fixtures"
  ...
end  

と記述すれば、省略できます。またfixtureを使用しないのであればコメントアウトしてしましましょう!

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe "User" do
    context "user.nameが生成されるテスト" do
      it "user.nameがhogeである" do
        user_name = create(:user).name
        expect(user_name).to eq "hoge"
      end
    end
  end
end 

FactoryGirlの呼び出し

build

buildしたオブジェクトを生成(DB保存なし)

FactoryGirl.build(:user)

create

createしたオブジェクトを生成(DB保存する)

FactoryGirl.create(:user)

attributes_for

hashを生成する

FactoryGirl.attributes_for(:user)

ネストしたFactoryGirl

共通部分は最初のブロック内で記述して、変更したい部分はネストさせて使い分けるやり方もある

spec/factories/user.rb
FactoryGirl.define do
  factory :user do
    available true

    factory :user_for_validation do
      name "hoge"
      ...
    end

    factory :user_for_method do
      name "fuga"
      ...
    end

  end
end

traitでカラムを変更

traitの機能を使うと、同じ名前で複数のバリエーションを用意することができる。
特定のカラムだけを変更できる。
複数カラム変更する場合は、楽だし可読性も上がる

spec/factories/user.rb
FactoryGirl.define do
  factory :user do
    name "hoge"

    trait :age_under_20 do
      age 18
      ...
    end

    trait :age_over_20 do
      age 35
      ...
    end

  end
end

FactoryGirlでオブジェクトを生成するとき、traitを第二引数で指定する

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe "User" do
    context "user.nameが生成されるテスト" do
      it "user.nameがhogeである" do
        user_age = FactoryGirl.create(:user, :age_under_20).age
        expect(user_age).to eq 18
      end
    end
  end
end

FactoryGirlでアソシエーションを定義

アソシエーションのテスト時には、関連を定義する必要がある。

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :posts
  ...
end

# app/models/post.rb
class Favorite < ActiveRecord::Base
  belongs_to :user
  ...
end

spec/factories/post.rb
FactoryGirl.define do
  factory :post do
    sequence(:title){ |n| "test_#{n}" }
    association :user
  end
end
spec/factories/user.rb
FactoryGirl.define do
  factory :post do
    title "test"

    after(:create) do |user, evaluator|
      create_list(:post, 5, user: user)
    end
  end
end

これで、create(:user)した時に、has_manyのpostオブジェクトも生成される。。はず。。

補足

sequence

自動採番してくれる
作成した順番でnにインクリメントした番号が入る

create_list

指定した第二引数の数分だけオブジェクトを作成
第三引数は、belongs_toで紐づいているオブジェクトを指定

evaluator

ブロック変数のevaluatorは全てのオブジェクトが格納される

after部分について

trait化することも可能。
検証しきれてません...

終わりに

ここら辺を覚えれば、初心者から初級者に繰り上げできると思います。
まずは公式ドキュメント!

殴り書きすぎたので、平日の夜にqiitaアップするもんじゃないな~と思いました。。。

参考url

factory_girl/GETTING_STARTED.md at master · thoughtbot/factory_girl
RailsでFactoryGirlを使ってみるメモ [俺の備忘録]
いまさら聞けないfactory_girl入門 - Grooves開発ブログ
RSpecにおけるFactoryGirlの使い方まとめ - Qiita
【解決済み】FactoryGirl の relation の定義の仕方がよくわからない>< - yoshiori.github.io