読者です 読者をやめる 読者になる 読者になる

あすたぴのブログ

astap(あすたぴ)のブログ

factory_girlの使い方を全体的にまとめた

factory_girlとは

factory_girlとは、 Rspec等の、Rubyのテストフレームワークで使用する、 テストデータ生成のためのライブラリ。

使い所

おもに、Activerecordのデータを生成することに長けている。 Activerecord以外のデータも生成可能だが、 生成ロジック等は自身で書くことになる。

併用ライブラリ

テストデータ生成といっても、データの中身は別途用意する必要がある。 そこで使われるのは、faker。 fakerの使い方は公式のReadmeを一部引用して軽く説明。

以下のような、記述で簡単に、ダミーデータを作成できる。

Faker::Name.name      #=> "Christophe Bartell"
Faker::Internet.email #=> "kirsten.greenholt@corkeryfisher.info"

fakerと組み合わせてfactorygirlを使っていく。

単一テーブルのデータ生成

テーブル

+-----------------+--------------+------+-----+---------+---------------------+
| Field           | Type         | Null | Key | Default | Extra               |
+-----------------+--------------+------+-----+---------+---------------------+
| user_id         | int(11)      | NO   | PRI | NULL    | auto_increment      |
| nickname        | varchar(100) | NO   |     | NULL    |                     |
| created_at      | datetime     | NO   |     | NULL    |                     |
| updated_at      | datetime     | NO   |     | NULL    |                     |
+-----------------+--------------+------+-----+---------+---------------------+

モデル

class User < ActiveRecord::Base
end

上記のような、usersテーブルがあったとする。 これを、factorygirlで生成する。

FactoryGirl.define do
  factory User do
    nickname { Faker::Name.name }
  end
end

上記がfactorygirlの定義

id, created_at, updated_at は activerecord側で生成されるので、 nicknameだけを指定すればよい。

user = create(:user)

生成時は上記のように書けばいい。

補助的なデータの生成

例えば、user生成時に指定したユーザーとフレンド関係になっていてほしいとか

Friendsテーブルがあるとする。

id
user_id
friend_id
created_at
updated_at
FactoryGirl.define do
  factory User do
    nickname { Faker::Name.name }
    
    factory :friend do
      transient do
        friend_id ''
      end
      
      after(:create) do |user, evaluator|
        Friend.new(user_id: user.id, friend_id: evaluator.friend_id)
      end
    end
  end
end
user = create(:user)
friend = create(:friend, friend_id: user.id)

factory定義中にfactoryを定義することができ、 これを行うとfactoryを継承することができる。

上記の場合は、Userを継承し:friendを作成している。

factoryの定義では、factoryの生成周りで、callbackが呼ばれる。

  • after(:build)
  • before(:create)
  • after(:create)
  • after(:stub)

stub以外は、activerecodのbuild,create前後のことだと考えてOK stubについては今回は扱わない。

transientは一時データを、初期から定義、外部から渡すことが可能。 今回は、friendになるユーザーのIDを外部から渡している。

最初からフレンドを持っているユーザーを生成する

ユーザー生成時に、フレンドもついでに作成したいケースもある。 factoryの定義に、traitを指定して、factory生成にパターンを持たせる事ができる。

FactoryGirl.define do
  factory User do
    nickname { Faker::Name.name }
    
    trait :with_friends do
      transient do
        friend_count 1
      end
      
      after(:create) do |user, evalutor|
        evalutor.friend_count.times.each do |_i|
          Friend.new(user_id: user.id, firned_id: create(:user).id)
        end
      end
    end
  end
end
user = create(:user, :with_friends, friend_count: 2)

これで、user生成時に、friendを指定数だけ一緒に生成する定義が出来る。 今回は追加データを生成するパターンに使っているが、 作成するデータのデータ内容のパターンに使うことも可能。

belongs_toが関係のデータ生成

Userモデルに従属しているテーブルがあったとします。

PostsテーブルとUserモデル、 Postモデル定義

id
user_id
created_at
updated_at
class User < ActiveRecord::Base
  has_many :posts
end
class Post < ActiveRecord::Base
  belongs_to :user
end

Postモデルのデータ生成時に、依存しているデータの生成も併せて行えます。

FactoryGirl.define do
  factory Post do
    user
  end
end
post = create(:post)

userモデルに従属しているということをPostファクトリ定義時に指定している。 これは、以下の記述と同様の意味になっている

FactoryGirl.define do
  factory Post do
    association :user, factory: :user
  end
end
user = create(:user)
poset = create(:post, user: user)

逆に、Userのデータはすでに生成済で、 そのUserに対してPostのデータを生成したい場合は上記のように、引数として渡すことができる。

従属している側から生成することはあんまりない気がするので、 たいていの場合は後者の記述。もしくは、friendのときのように、 :with_postsみたいにtraitでpost持ちのUserを生成する定義を書いたほうが使いやすい。

その他

別のテーブルの関係とかは、だいたい、after(:create)とか before(:create)とか、使えば、なんでも作れる。

traitでパターンを定義したが、複数パターンを組み合わせたい場合は、 別途定義ができる

factory :male_admin,   traits: [:male, :admin]   # login will be "admin-John Doe"
factory :female_admin, traits: [:admin, :female] # login will be "Jane Doe (F)"  

単一のtraitの場合は、factoryの引数に指定していたが、 複数を組み合わせる場合は別factoryとなる。

user = create(:male_admin)

まとめ

factorygirlの機能を組み合わせると、細かくパターンごとにテストデータの生成が可能になる。 うまく定義することで、テストコードのデータ作成部分がスッキリするのでおすすめ。

ここに書いたことは、全部公式にのっている