Factory Girl 3.x メモ

Rails 3.2.2 + RSpec 2.9.0 + Factory Gril 3.1.0。

##インストール

```sh
gem install factory_girl_rails
```

```ruby:GemFile
group :development, :test do
  gem 'rspec'
  gem 'rspec-rails'
  gem 'guard-rails'
  gem 'factory_girl_rails'
end
```

```sh
bundle install
```

##基本的な使い方

```ruby:spec/factories.rb
FactoryGirl.define do

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

end
```

```ruby: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を省略できるよう設定

```ruby:spec/spec_helper.rb
config.include FactoryGirl::Syntax::Methods
```

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

```ruby
factory :hoge do
  first_name 'fuga'
  last_name 'hige'
  full_name '#{first_name}.#{last_name}'
end
```

##createした後に属性を操作

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

```ruby
factory :member1, class: Member do
  name :member1.to_s
  after_create do |m, evaluator|
    m.name.upcase!
  end
end
```

##連番付きのデータを生成

```ruby:spec/factories.rb
# 連番付きの名前を生成
sequence :member_names do |n|
  "name_#{n}"
end

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

```ruby
# 10件作る
10.times do
  FactoryGirl.create(:sequence_members)
end
```

***

## 属性の上書き

```ruby
factory :member_base, class: Member do
  name :member_base.to_s
end

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

## 別名を付けられる

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

***

## 一時的な属性

```ruby

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

```ruby
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
```

```ruby
# どちらでも結果は同じ
FactoryGirl.build(:player_base)
FactoryGirl.build(:member_base, :player_base)
```

## 同じデータを複数作る

```ruby
FactoryGirl.build_list(:member_base, 100)
FactoryGirl.create_list(:member_base, 100)
```

## コールバック

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

- after_build
- after_create
- after_stub

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

```ruby
factory :member, class: Member do
  after_build do |m|
  end
  after_create do |m, evalator|
  end
  after_stub {}
end
```

## 継承みたいなの

```ruby
factory :member, class: Member do
  name 'hoge'
end

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

## 修正

```ruby
FactoryGirl.define  do
 factory :member_base do
   name 'hoge'
 end
end

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

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

```ruby
FactoryGirl.attributes_for(:member)
```

## サンプル

スポーツなんかのチームを管理することを想定。
チームの配下にメンバーが複数属する。

```ruby
# -*- 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)
```