Edited at

Factory Girl 3.x メモ

More than 5 years have passed since last update.

Rails 3.2.2 + RSpec 2.9.0 + Factory Gril 3.1.0。


インストール

gem install factory_girl_rails


GemFile

group :development, :test do

gem 'rspec'
gem 'rspec-rails'
gem 'guard-rails'
gem 'factory_girl_rails'
end

bundle install


基本的な使い方


spec/factories.rb

FactoryGirl.define do

# 使用する際は:team1を指定する
# シンボルがそのままクラス名ならclassは省略できる
# name、addressがモデルの属性
factory :team1, class: Team do
name :team1.to_s
address '住所'
end

end



spec/models/team_spec.rb

require 'spec_helper'

describe Team do
it 'can save.' do
# :team1はfactories.rbで指定したシンボル
# buildだとDBに反映されない
# createだと反映される
team = FactoryGirl.build(:team1)
team.save.should_not be_false
end
end



specファイルでFactoryGirlを省略できるよう設定


spec/spec_helper.rb

config.include FactoryGirl::Syntax::Methods



他の属性に依存した値を作る

factory :hoge do

first_name 'fuga'
last_name 'hige'
full_name '#{first_name}.#{last_name}'
end


createした後に属性を操作

3.3で記法が変更。詳しくはコメント参照。

factory :member1, class: Member do

name :member1.to_s
after_create do |m, evaluator|
m.name.upcase!
end
end


連番付きのデータを生成


spec/factories.rb

# 連番付きの名前を生成

sequence :member_names do |n|
"name_#{n}"
end

factory :sequence_members, class: Member do
# {}で囲むと遅延評価
# {}がないと連番付きのデータで更新されないので要注意
name { FactoryGirl.generate(:member_names) }
end


# 10件作る

10.times do
FactoryGirl.create(:sequence_members)
end



属性の上書き

factory :member_base, class: Member do

name :member_base.to_s
end

m = FactoryGirl.build(:member_base, { name: 'hogehogehoge' })


別名を付けられる

factory :member_base, aliases: [:player_base, :manager_base], class: Member do 

end



一時的な属性


factory :member_base, aliases: [:player_base, :manager_base], class: Member do
# 一時的な属性を定義
# 以降で参照できる
# また通常の属性と同様に外から上書きもできる
ignore do
upcased true
end

name :member_base.to_s

after_create do |m, evaluator|
# 上で定義した属性を参照
m.name.upcase! if evaluator.upcased
end
end
end


traits

factory :member_base, class: Member do

name :member_base.to_s

# 値を設定済みの属性に別名を付ける感じ
trait :type_player do
member_type 0
end

trait :type_manager do
member_type 1
end

# buildなどをする際は、
# build(:player_base)でできる
# factoryが入れ子になっているけど指定する際には関係なさげ
factory :player_base, traits: [:type_player]
factory :manager_base, traits: [:type_manager]
end
end

# どちらでも結果は同じ

FactoryGirl.build(:player_base)
FactoryGirl.build(:member_base, :player_base)


同じデータを複数作る

FactoryGirl.build_list(:member_base, 100)

FactoryGirl.create_list(:member_base, 100)


コールバック

3.3から変更あり。コメント参照。


  • after_build

  • after_create

  • after_stub

それぞれbuild、create、build_stubbedされた際に実行される。

factory :member, class: Member do

after_build do |m|
end
after_create do |m, evalator|
end
after_stub {}
end


継承みたいなの

factory :member, class: Member do

name 'hoge'
end

# after_createとかの動きがよくわからない
# 上書きできない?
factory :member_ex, parent: :member do
name 'hogehoge'
end


修正

FactoryGirl.define  do

factory :member_base do
name 'hoge'
end
end

FactoryGirl.modify do
factory :member_base do
name 'hogehoge'
end
end


ハッシュで生成したデータを取得

FactoryGirl.attributes_for(:member)


サンプル

スポーツなんかのチームを管理することを想定。

チームの配下にメンバーが複数属する。

# -*- coding: utf-8 -*-

require 'faker/japanese'
FactoryGirl.define do

# メンバテーブルの基本データ
factory :member_base, class: Member do
name { Faker::Japanese::Name.name }

trait :type_player do
member_type 1
end

trait :type_manager do
member_type 2
end

# 選手
factory :player, traits: [:type_player]
# 指導者
factory :manager, traits: [:type_manager]
end

# チームとメンバーの中間テーブル
factory :team_member do
team_id 0
member_id 0
end

# チームの基本データ
factory :team_base, class: Team do
ignore do
member_count 0
end
name {"チーム:#{Faker::Japanese::Name.name}"}

after_create do |team, evaluator|
# 指導者を作成
manager = FactoryGirl.create(:manager)
FactoryGirl.create(:team_member, {team_id: team.id, member_id: manager.id})

# 指定された人数分の選手を作成
if evaluator.member_count > 0
members = FactoryGirl.create_list(:player, evaluator.member_count)
members.each do |player|
FactoryGirl.create(:team_member, {team_id: team.id, member_id: player.id})
end
end
end
end
end

# 使い方
team = FactoryGirl.create(:team_base, member_count: 30)