web-k.log

RubyやWebをメインに技術情報をのせていきます

Fabrication

| Comments

ここでは、Fabrication のサイトを日本語に直して、自己解釈して補完しながら説明していきます。

Fabricationとは

これはオブジェクト生成ライブラリで、 オブジェクトの概略だけを定義し、素早くオブジェクトを使うことができるものである。 サポートしているオブジェクトタイプは以下のものなどがある。

  • ActiveRecord Models
  • Mongoid Documents
  • Sequel Models
  • DataMapper Resources
  • ・・・

設定

Gemfile に Fabrication を記載し、bundle install すれば使える

1
gem 'fabrication'

デフォルトでは以下にFabrication関連のソースを置くと、自動ロードされる。

1
2
spec/fabricators/**/*fabricator.rb
test/fabricators/**/*fabricator.rb

設定を変更したい場合は以下のように、Fabrication.configureで設定変更できる。

1
2
3
4
Fabrication.configure do |config|
  config.fabricator_path = 'data/fabricators' #Fabrication関連の定義を置くパス
  config.path_prefix = Rails.root #ファイルシステムへの許可範囲
end

引数

1
2
class Person; end
Fabricator(:person) #引数がFabricatiorオブジェクトになる。クラス名のシンボルである必要がある
1
Fabricator(:adult, from: :person) #from: :symbolized_class_nameのクラス名を変えて:adultというFabricatiorオブジェクトが定義できる

属性

Fabricator ブロックには変数が必要ではないが、1つ提供される。属性リスト作成時に、宣言もされる。

1
2
3
4
Fabricator(:person) do
  name 'Greg Graffin'
  profession 'Professor/Musician'
end

属性には変数を渡すことができる

1
2
3
4
Fabricator(:person) do
  name { Faker::Name.name }
  profession { %w(Butcher Baker Candlestick\ Maker).sample }
end

属性は処理順に宣言され、上記フィールドのブロック変数を用いることができる。

1
2
3
4
Fabricator(:person) do
  name { Faker::Name.name }
  email { |attrs| "#{attrs[:name].parameterize}@example.com" }
end

予約語

予約語名をブロック変数と一緒に使うことで属性として参照できる

1
2
3
Fabricator(:person) do |f|
  f.alias 'James Bond'
end

関連

他のFabricatorに関連付ける場合は、属性名を書くだけでいい。 これで、「belongs_to」の関連を表現できる。

1
2
3
4
5
6
7
Fabricator(:person) do
  vehicle
end
↑↓等価
Fabricator(:person) do
  vehicle { Fabricate(:vehicle) }
end
1
2
3
4
5
6
7
Fabricator(:person) do
  ride(fabricator: :vehicle)
end
↑↓等価
Fabricator(:person) do
  ride { Fabricate(:vehicle) }
end

countパラメータを使うことで、配列オブジェクトを生成できる。

1
2
3
4
Fabricator(:person) do
  open_souce_projects(count: 5)
  children(count: 3) { |attrs, i| Fabricate(:person, name: "Kid #{i}") }
end

継承

他の Fabricators から属性を継承する場合は、「:from」 を使う

1
2
3
4
5
6
7
Fabricator(:llc, from: :company) do #クラスの属性とその値を全て継承する
  type "LLC"
end

Fabricator(:llc, class_name: :company) do # class_name: でクラスの属性のみを継承する
  type "LLC"
end

初期化

オブジェクトの初期化を通常の方法でしてほしくないときは、 initialize_withを以下のようにオーバーライドすればよい。

1
2
3
4
Fabricator(:car) do
  initialize_with { Manufacturer.produce(:new_car) }
  color 'red'
end

コールバック

Fabricationのビルドにフックするには、after_buildafter_create を使う。

1
2
3
4
Fabricator(:place) do
  after_build { |place| place.geolocate! } #ビルド後=保存する前
  after_create { |place| Fabricate(:restaurant, place: place) } #保存した後
end

オブジェクトに引数を与えたときのコンストラクタでコールバックするときは、on_initを使う。

1
2
3
Fabricator(:location) do
  on_init { init_with(30.284167, -81.396111) }
end

コールバックはスタックになっているので、並列にFabricatorを宣言できるし、継承しても大丈夫。

エイリアス

Fabricatior呼び出し時に:aliasesオプションをつけるとエイリアスが付けれる。

1
Fabricator(:thingy, aliases: [:widget, :wocket]) #Fabricateを :thingy,:widget, :wocketどれでも呼び出せる

一時属性

Fabricator内で一時属性を変数として持てるが、クラス生成時にはセットされない。 一時属性は、クラスが生成されるまでの間は普通の属性と同じように扱えるが、生成時に取り除かれる。

1
2
3
4
5
6
7
8
9
10
Fabricator(:city) do
  transient :asian
  name { |attrs| attrs[:asian] ? "Tokyo" : "Stockholm" }
end
Fabricate(:city, asian: true)
# => <City name: 'Tokyo'>

Fabricator(:the_count) do
  transient :one, :two, :three #複数定義可
end

リロード

Fabricationがロードされた状態にリセットする

1
Fabrication.clear_definitions

基本

Fabricateオブジェクトを作成する簡単な方法は、クラス名を渡すだけでいい。

1
Fabricate(:person)

これで、PersonのインスタンスがFabricatorとして定義される。 Fabricator作成時に、引数としてハッシュを渡せば、属性の追加や、上書きができる。

1
Fabricate(:person, first_name: "Corbin", last_name: "Dallas")

Fabricating With Blocks

Fabricateのブロックの引数にハッシュ値を渡せば、オブジェクト生成時に定義され利用できる。

1
2
3
4
Fabricate(:person, name: "Franky Four Fingers") do
  addiction "Gambling"
  fingers(count: 9)
end

ビルド

データベースにオブジェクトを持続させたくないときは Fabricate.build を使う。

1
Fabricate.build(:person)

下記のように、Fabricate.build内でFabricateが呼ばれていてもオブジェクトは持続せず、buildのときの動作と同じになる。

1
2
3
Fabricate.build(:person) do
  cars { 2.times { Fabricate(:car) } }
end

属性のハッシュ

オブジェクトを生成せずに、属性だけを生成してハッシュで返したい場合は以下のようにする。

1
Fabricate.attributes_for(:company)

Sequences

Sequencesはそのプロセスにおいての、ユニークな連続した数値が得られる。 Sequencesは指定がなければ、0からはじまる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Fabricate.sequence #実行するたびにインクリメントされていく
# => 0 #1回目
# => 1 #2回目
# => 2 #3回目

Fabricate.sequence(:name) #引数を渡せば独自の数値でインクリメントされる
# => 0
# => 1
# => 2

Fabricate.sequence(:number, 99) #第2引数の数値は開始の数値
# => 99
# => 100
# => 101

Fabricate.sequence(:name) { |i| "Name #{i}" } #ブロックで渡してもインクリメントされる
# => "Name 0"
# => "Name 1"
# => "Name 2"

Fabricate(:person) do #例
  ssn { sequence(:ssn, 111111111) }
  email { sequence(:email) { |i| "user#{i}@example.com" } }
end
# => <Person ssn: 111111111, email: "user0@example.com">
# => <Person ssn: 111111112, email: "user1@example.com">
# => <Person ssn: 111111113, email: "user2@example.com">

Rails 3

Rails 3でFabricatorsをモデル生成時に一緒に生成したい場合は、config/application.rb に設定を書く。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# rspecの場合
config.generators do |g|
  g.test_framework      :rspec, fixture: true
  g.fixture_replacement :fabrication
end
# test/unitの場合
config.generators do |g|
  g.test_framework      :test_unit, fixture_replacement: :fabrication
  g.fixture_replacement :fabrication, dir: "test/fabricators"
end
# minitestの場合
config.generators do |g|
  g.test_framework      :mini_test, fixture_replacement: :fabrication
  g.fixture_replacement :fabrication, dir: "test/fabricators"
end

上記設定後、下記コマンドでFabricationのファイルができる。

1
2
3
4
$ rails generate model widget #コマンド
spec/fabricators/widget_fabricator.rb
 Fabricator(:widget) do #中身
 end

参考リンク

Comments