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の機能を組み合わせると、細かくパターンごとにテストデータの生成が可能になる。 うまく定義することで、テストコードのデータ作成部分がスッキリするのでおすすめ。
ここに書いたことは、全部公式にのっている