Help us understand the problem. What is going on with this article?

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)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away