FactoryBot 入門 [翻訳][Railsの場合まとめ]

この記事は、[factory_bot](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md) の `Rails` の場合だけを翻訳&わかりやすく補足を追加したものになります。

## Gemfileを更新する（Update Your Gemfile）

Railsを使用している場合：

```ruby
gem "factory_bot_rails"
```

Gemfileが更新されたら、バンドルを更新する必要があります。

```shell
bundle install
```

## テストスイートを構成する（Configure your test suite）

### RSpec

Railsを使用している場合、次の設定を `spec/support/factory_bot.rb`に追加し、`rails_helper.rb`にそのファイルが必要であることを確認してください：

```ruby
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end
```

※テストスイートに `FactoryBot::Syntax::Methods`を含めない場合、すべてのfactory_botメソッドの前に` FactoryBot`を付ける必要があります。

```ruby
FactoryBot.create(:user)

# OK
create(:user)
```

## ファクトリーの定義（Defining factories）

各ファクトリーには、名前と一連の属性があります。
名前は、デフォルトでオブジェクトのクラスを推測するために使用されます。

```ruby
# これはUserクラスを推測します
FactoryBot.define do
  factory :user do
    first_name { "John" }
    last_name  { "Doe" }
    admin { false }
  end
end
```

クラスを明示的に指定することもできます。

```ruby
# これはUserクラスを使用します（そうでなければ、Adminが推測されます）
factory :admin, class: User do
```

### 定数が利用できない場合
（たとえば、モデルのロードを待機するRailsエンジンを使用している場合）
シンボルまたは文字列を渡すこともできます。
オブジェクトの構築を開始すると、`factory_bot`が後で定数化されます。

```ruby
# Doorkeeper::AccessTokenがまだロードされていなくても問題ありません
factory :access_token, class: "Doorkeeper::AccessToken"
```

Rubyのブロック構文のため、属性を「ハッシュ」として定義します。
（例、serialized / JSON列には、2組の中括弧が必要です。

```ruby
factory :program do
  configuration { { auto_resolve: false, auto_define: true } }
end
```

そのクラスのインスタンスを作成するために必要な属性の最も単純なセットを提供する各クラスに1つのファクトリーを持つことを強くお勧めします。
ActiveRecordオブジェクトを作成している場合、検証によって必要とされ、デフォルトを持たない属性のみを提供する必要があることを意味します。
継承を通じて他のファクトリーを作成して、各クラスの一般的なシナリオをカバーできます。

`注：同じ名前の複数のファクトリーを定義しようとすると、エラーが発生します。`

ファクトリーはどこでも定義できますが、後で自動的にロードされます。
下記の場所にファクトリーが定義されている場合、 `FactoryBot.find_definitions`を呼び出します。

```ruby
spec/factories/*.rb
```

## ファクトリーの使用方法（Using factories）

`factory_bot` は、いくつかの異なるビルドをサポートしています。

```ruby
# 保存されていないUserインスタンスを返します
user = build(:user)

# 保存されたUserインスタンスを返します
user = create(:user)

# Userインスタンスの構築に使用できる属性のハッシュを返します
attrs = attributes_for(:user)

# すべての定義済み属性がスタブアウトされたオブジェクトを返します
stub = build_stubbed(:user)

# 上記のメソッドのいずれかにブロックを渡すと、戻りオブジェクトが生成されます
create(:user) do |user|
  user.posts.create(attributes_for(:post))
end
```

使用するファクトリーに関係なく、ハッシュを渡すことで定義済みの属性をオーバーライドできます。

```ruby
# ユーザーインスタンスをビルドし、first_nameプロパティをオーバーライドします
user = build(:user, first_name: "Joe")
user.first_name
# => "Joe"
```

## エイリアス（Aliases）

`factory_bot`を使用すると、既存のファクトリーのエイリアスを定義して、再利用しやすくすることができます。
例えば、Postオブジェクトに実際にUserクラスのインスタンスを参照するauthor属性がある場合に便利です。
通常、`factory_bot`は関連付け名からファクトリー名を推測できますが、この場合、作成者ファクトリーを探しますが無駄です。
そのため、エイリアスネームの下で使用できるように、ユーザーファクトリーにエイリアスを作成します。

```ruby
factory :user, aliases: [:author, :commenter] do
  first_name { "John" }
  last_name { "Doe" }
  date_of_birth { 18.years.ago }
end

factory :post do
  author
  # instead of
  # association :author, factory: :user
  title { "How to read a book effectively" }
  body { "There are five steps involved." }
end

factory :comment do
  commenter
  # instead of
  # association :commenter, factory: :user
  body { "Great article!" }
end
```

## 依存属性（Dependent Attributes）

属性は、エバリュエーターを使用して他の属性の値に基づくことができます
動的属性ブロックに渡されます：

```ruby
factory :user do
  first_name { "Joe" }
  last_name  { "Blow" }
  email { "#{first_name}.#{last_name}@example.com".downcase }
end

create(:user, last_name: "Doe").email
# => "joe.doe@example.com"
```

## 一時的な属性（Transient Attributes）

一時的な属性をファクトリーに渡すことで、DRY原則でコードを書くことができます。

```ruby
factory :user do
  transient do
    rockstar { true }
    upcased { false }
  end

  name { "John Doe#{" - Rockstar" if rockstar}" }
  email { "#{name.downcase}@example.com" }

  after(:create) do |user, evaluator|
    user.name.upcase! if evaluator.upcased
  end
end

create(:user, upcased: true).name
#=> "JOHN DOE - ROCKSTAR"
```
一時属性は、モデルに設定し属性が存在する場合でも、上書きしようとする場合でも。
`attributes_for`内では無視されません。

## メソッド名/予約語の属性（Method Name/Reserved Word Attributes）

属性が既存のメソッドまたは予約語（[DefinitionProxy](https://github.com/thoughtbot/factory_bot/blob/master/lib/factory_bot/definition_proxy.rb)クラスのすべてのメソッド）と競合する場合、それらを定義できます。`add_attribute`

```ruby
factory :dna do
  add_attribute(:sequence) { 'GATTACA' }
end

factory :payment do
  add_attribute(:method) { 'paypal' }
end
```

## 継承（Inheritance）

ファクトリーをネストすることにより、共通の属性を繰り返すことなく、同じクラスに対して複数のファクトリーを簡単に作成できます。

```ruby
factory :post do
  title { "A title" }

  factory :approved_post do
    approved { true }
  end
end

approved_post = create(:approved_post)
approved_post.title    # => "A title"
approved_post.approved # => true
```

親モデルを明示的に割り当てることもできます。

```ruby
factory :post do
  title { "A title" }
end

factory :approved_post, parent: :post do
  approved { true }
end
```

上記のように、各クラスの基本的なファクトリーを定義することをお勧めします。
作成に必要な属性のみが含まれます。次に、より具体的に作成します。
この基本的な親から継承するファクトリーの定義は、DRY原則になります。

## 関連付け（Associations）

ファクトリー内で関連付けを設定することは可能です。
ファクトリー名が関連付け名と同じ場合、ファクトリー名は省略できます。

```ruby
factory :post do
  # ...
  author
end
```

別のファクトリーを指定するか、属性をオーバーライドすることもできます。

```ruby
factory :post do
  # ...
  association :author, factory: :user, last_name: "Writely"
end
```

`factory_bot 5`では、親オブジェクトへの関連付けはデフォルトで同じビルド戦略を使用します。

```ruby
FactoryBot.define do
  factory :author

  factory :post do
    author
  end
end

post = build(:post)
post.new_record?        # => true
post.author.new_record? # => true

post = create(:post)
post.new_record?        # => false
post.author.new_record? # => false
```

これは、以前のバージョンのデフォルトの動作とは異なります
`factory_bot` のアソシエーションは常に戦略と一致するとは限りません。
以前のバージョンを引き続き使用する場合は、下記の設定をすることで解消できます。
`use_parent_strategy`設定オプションを` false`に設定します。

```ruby
FactoryBot.use_parent_strategy = false

# ユーザーと投稿を作成して保存します。
post = create(:post)
post.new_record?        # => false
post.author.new_record? # => false

# ユーザーをビルドして保存し、ビルドしますが投稿は保存しません。
post = build(:post)
post.new_record?        # => true
post.author.new_record? # => false
```

関連付けられたオブジェクトを保存しないようにするには、ファクトリーで `strategy：：build`を指定します。

```ruby
FactoryBot.use_parent_strategy = false

factory :post do
  # ...
  association :author, factory: :user, strategy: :build
end

# ユーザーを作成してから、投稿を作成しますが、どちらも保存しません
post = build(:post)
post.new_record?        # => true
post.author.new_record? # => true
```

`strategy：：build`オプションは`association`の明示的な呼び出しに渡す必要があることに注意してください。
暗黙の関連付けでは使用できません。

```ruby
factory :post do
  # ...
  author strategy: :build # <<<これは動作しません。author_idがnilになります。
```

「has_many」関係のデータの生成はもう少し複雑です。
必要な柔軟性の量に応じて、しかし関連データの生成で確実な例があります。

```ruby
FactoryBot.define do

  # ユーザーの `belongs_to`アソシエーションでファクトリーをポストする
  factory :post do
    title { "Through the Looking Glass" }
    user
  end

  # 投稿が関連付けられていないユーザーファクトリー
  factory :user do
    name { "John Doe" }

    # user_with_postsは、ユーザーの作成後に投稿データを作成します
    factory :user_with_posts do
      # posts_countは一時的な属性として宣言されており、
      # ファクトリーの属性、およびエバリュエーターを介したコールバック
      transient do
        posts_count { 5 }
      end

      # after（：create）は2つの値を生成します。ユーザーインスタンス自体と
      # 一時的なものを含む、工場からのすべての値を保存するエバリュエーター
      # 属性; `create_list`の2番目の引数はレコード数です
      # 作成し、ユーザーが投稿に適切に関連付けられていることを確認します
      after(:create) do |user, evaluator|
        create_list(:post, evaluator.posts_count, user: user)
      end
    end
  end
end
```

これにより、下記のことが可能になります。

```ruby
create(:user).posts.length # 0
create(:user_with_posts).posts.length # 5
create(:user_with_posts, posts_count: 15).posts.length # 15
```

「has_and_belongs_to_many」関係のデータの生成は非常に似ています。
上記の「has_many」関係に、わずかな変更を加えて、単一ではなく、
モデルの複数形の属性名に対するオブジェクトの配列を生成します。

以下は、単一バージョンの属性名のオブジェクトを介して関連する2つのモデル作成の例です。
 `has_and_belongs_to_many`：

```ruby
FactoryBot.define do

  # プロファイルの「belongs_to」関連付けを持つ言語ファクトリー
  factory :language do
    title { "Through the Looking Glass" }
    profile
  end

  # 言語が関連付けられていないプロファイルファクトリー
  factory :profile do
    name { "John Doe" }

    # profile_with_languagesは、profile が作成された後にlanguagesを作成します。
    factory :profile_with_languages do
      # languages_countは無視される属性として宣言されており、
      # ファクトリーの属性、およびエバリュエーターを介したコールバック
      transient do
        languages_count { 5 }
      end

      # after(:create)は2つの値を生成します。プロファイルインスタンス自体
      # エバリュエーター。ファクトリーを含むすべての値を格納します。
      # 属性を無視しました。 `create_list`の2番目の引数は、
      # 作成するレコード。プロファイルが適切に関連付けられていることを確認します。
      after(:create) do |profile, evaluator|
        create_list(:language, evaluator.languages_count, profiles: [profile])
      end
    end
  end
end
```

これにより、下記のことが可能になります。

```ruby
create(:profile).languages.length # 0
create(:profile_with_languages).languages.length # 5
create(:profile_with_languages, languages_count: 15).languages.length # 15
```

### ポリモーフィックな関連付け

ポリモーフィックな関連付けは、特性を使用して処理できます。

```ruby
FactoryBot.define do
  factory :video
  factory :photo

  factory :comment do
    for_photo # default to the :for_photo trait if none is specified

    trait :for_video do
      association :commentable, factory: :video
    end

    trait :for_photo do
      association :commentable, factory: :photo
    end
  end
end
```

これにより、下記のことが可能になります。

```ruby
create(:comment)
create(:comment, :for_video)
create(:comment, :for_photo)
```


## シーケンス（Sequences）

特定の形式（たとえば、電子メールアドレス）の一意の値はシーケンスを使用して生成されます。

```ruby
＃新しいシーケンスを定義
FactoryBot.define do
  sequence :email do |n|
    "person#{n}@example.com"
  end
end

generate :email
# => "person1@example.com"

generate :email
# => "person2@example.com"
```

シーケンスは、動的属性で使用できます。

```ruby
factory :invite do
  invitee { generate(:email) }
end
```

または暗黙的な属性としてシーケンスを定義すると、シーケンスと同じ名前のファクトリーができます。

```ruby
factory :user do
  email # `email {generate（：email）}`と同じ
end
```

また、特定のファクトリーでのみ使用されるインラインシーケンスを定義することもできます。

```ruby
factory :user do
  sequence(:email) { |n| "person#{n}@example.com" }
end
```

初期値をオーバーライドすることもできます。

```ruby
factory :user do
  sequence(:email, 1000) { |n| "person#{n}@example.com" }
end
```

ブロックがない場合、値はその初期値から始まり、自動的に増加します。

```ruby
factory :post do
  sequence(:position)
end
```

シーケンスにはエイリアスも設定できます。シーケンスエイリアスは同じカウンターを共有します。

```ruby
factory :user do
  sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end

# senderおよび：receiverによって共有される：emailの値カウンターを増加します
generate(:sender)
```

エイリアスを定義し、カウンターにデフォルト値（1）を使用します

```ruby
factory :user do
  sequence(:email, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end
```

値の設定：

```ruby
factory :user do
  sequence(:email, 'a', aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end
```

値は `＃next`メソッドをサポートする必要があります。
ここでは、次の値は「a」、「b」などになります。

シーケンスは `FactoryBot.rewind_sequences`で巻き戻すこともできます。
これにより、登録されているすべてのシーケンスが巻き戻されます。

```ruby
sequence(:email) {|n| "person#{n}@example.com" }

generate(:email) # "person1@example.com"
generate(:email) # "person2@example.com"
generate(:email) # "person3@example.com"

FactoryBot.rewind_sequences

generate(:email) # "person1@example.com"
```


## 特性（Traits）

`trait`を使用すると、どのファクトリーにも属性をグループ化してから適用できます

```ruby
factory :user, aliases: [:author]

factory :story do
  title { "My awesome story" }
  author

  trait :published do
    published { true }
  end

  trait :unpublished do
    published { false }
  end

  trait :week_long_publishing do
    start_at { 1.week.ago }
    end_at { Time.now }
  end

  trait :month_long_publishing do
    start_at { 1.month.ago }
    end_at { Time.now }
  end

  factory :week_long_published_story,    traits: [:published, :week_long_publishing]
  factory :month_long_published_story,   traits: [:published, :month_long_publishing]
  factory :week_long_unpublished_story,  traits: [:unpublished, :week_long_publishing]
  factory :month_long_unpublished_story, traits: [:unpublished, :month_long_publishing]
end
```

特性は、暗黙的な属性として使用できます。

```ruby
factory :week_long_published_story_with_title, parent: :story do
  published
  week_long_publishing
  title { "Publishing that was started at #{start_at}" }
end
```

`注：暗黙的な属性として特性を定義することは機能しないことに注意してください。`

同じ属性を定義する特性は、`AttributeDefinitionErrors`を発生させません。
最新の属性を定義する特性が優先​​されます。

```ruby
factory :user do
  name { "Friendly User" }
  login { name }

  trait :male do
    name { "John Doe" }
    gender { "Male" }
    login { "#{name} (M)" }
  end

  trait :female do
    name { "Jane Doe" }
    gender { "Female" }
    login { "#{name} (F)" }
  end

  trait :admin do
    admin { true }
    login { "admin-#{name}" }
  end

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

サブクラスの`trait`によって付与された個々の属性をオーバーライドすることもできます。

```ruby
factory :user do
  name { "Friendly User" }
  login { name }

  trait :male do
    name { "John Doe" }
    gender { "Male" }
    login { "#{name} (M)" }
  end

  factory :brandon do
    male
    name { "Brandon" }
  end
end
```

`factory_bot`からインスタンスを構築するときに、特性をシンボルのリストとして渡すこともできます。

```ruby
factory :user do
  name { "Friendly User" }

  trait :male do
    name { "John Doe" }
    gender { "Male" }
  end

  trait :admin do
    admin { true }
  end
end

# 性別が「男性」で名前が「Jon Snow」の管理ユーザーを作成します
create(:user, :admin, :male, name: "Jon Snow")
```

この機能は、「build」、「build_stubbed」、「attributes_for」、および「create」で機能します。

`create_list`および` build_list`メソッドもサポートされています。

```ruby
# 複数のレコード作成
factory :user do
  name { "Friendly User" }

  trait :admin do
    admin { true }
  end
end

# 性別が「男性」で名前が「Jon Snow」の3人の管理ユーザーを作成します
# 2つ目のパラメーターとして作成/ビルドするインスタンスの数(3)
# 複数のレコード作成
create_list(:user, 3, :admin, :male, name: "Jon Snow")
```

特性は、関連付け（associations）でも簡単に使用できます。

```ruby
factory :user do
  name { "Friendly User" }

  trait :admin do
    admin { true }
  end
end

factory :post do
  association :user, :admin, name: 'John Doe'
end

# 「John Doe」という名前の管理ユーザーを作成します
create(:post).user
```

ファクトリーとは異なる関連付け名を使用している場合：

```ruby
factory :user do
  name { "Friendly User" }

  trait :admin do
    admin { true }
  end
end

factory :post do
  association :author, :admin, factory: :user, name: 'John Doe'
  # or
  association :author, factory: [:user, :admin], name: 'John Doe'
end

# 「John Doe」という名前の管理ユーザーを作成します
create(:post).author
```

特性を他の特性内で使用して、それらの属性を混在させることができます。

```ruby
factory :order do
  trait :completed do
    completed_at { 3.days.ago }
  end

  trait :refunded do
    completed
    refunded_at { 1.day.ago }
  end
end
```

最後に、特性は一時的な属性を受け入れることができます。

```ruby
factory :invoice do
  trait :with_amount do
    transient do
      amount { 1 }
    end

    after(:create) do |invoice, evaluator|
      create :line_item, invoice: invoice, amount: evaluator.amount
    end
  end
end

create :invoice, :with_amount, amount: 2
```

コールバック（Callbacks）
---------

`factory_bot`は、いくつかのコードを挿入するための4つのコールバックを使用可能です。

* after(:build)-ファクトリーのビルド後に呼び出されます（ `FactoryBot.build`、` FactoryBot.create`を介して）
* before(:create)-ファクトリーが保存される前に呼び出されます（ `FactoryBot.create`を介して）
* after(:create)-ファクトリーが保存された後に呼び出されます（ `FactoryBot.create`を介して）
* after(:stub)-ファクトリーがスタブ化された後に呼び出されます（ `FactoryBot.build_stubbed`を介して）

例：

```ruby
# ビルド後に generate_hashed_pa​​ssword メソッドを呼び出すファクトリーを定義します。
factory :user do
  after(:build) { |user| generate_hashed_password(user) }
end
```

ブロック内にユーザーのインスタンスがあることに注意してください。
これは便利です。
同じファクトリーで複数のタイプのコールバックを定義することもできます。

```ruby
factory :user do
  after(:build)  { |user| do_something_to(user) }
  after(:create) { |user| do_something_else_to(user) }
end
```

ファクトリーは、同じ種類のコールバックをいくつでも定義できます。
これらのコールバックは、指定された順序で実行されます。

```ruby
factory :user do
  after(:create) { this_runs_first }
  after(:create) { then_this }
end
```

「create」を呼び出すと、「after_build」と「after_create」の両方のコールバックが呼び出されます。

また、標準属性と同様に、子ファクトリーは親ファクトリーからコールバックを継承します（定義することもできます）。

ブロックを実行するために複数のコールバックを割り当てることができます。
これは、同じコードを実行するさまざまな戦略を構築する場合に便利です（すべての戦略で共有されるコールバックがないため）。

```ruby
factory :user do
  callback(:after_stub, :before_create) { do_something }
  after(:stub, :create) { do_something_else }
  before(:create, :custom) { do_a_third_thing }
end
```

すべてのファクトリーのコールバックをオーバーライドするには、`FactoryBot.define`で定義します。

```ruby
FactoryBot.define do
  after(:build) { |object| puts "Built #{object}" }
  after(:create) { |object| AuditLog.create(attrs: object.attributes) }

  factory :user do
    name { "John Doe" }
  end
end
```

`Symbol＃to_proc`に依存するコールバックを呼び出すこともできます：

```ruby
# app/models/user.rb
class User < ActiveRecord::Base
  def confirm!
    # ユーザーアカウントを確認
  end
end

# spec/factories.rb
FactoryBot.define do
  factory :user do
    after :create, &:confirm!
  end
end

create(:user) # ユーザーを作成して確認する
```

## ファクトリーの変更（Modifying factories）

一連のファクトリー（たとえば、gem開発者から）を与えられたが、それらをアプリケーションにより適合させるために変更したい場合は、次のことができます。
子ファクトリーを作成して属性を追加する代わりに、そのファクトリーを変更します。

`gem` がユーザーファクトリーを提供する場合：

```ruby
FactoryBot.define do
  factory :user do
    full_name { "John Doe" }
    sequence(:username) { |n| "user#{n}" }
    password { "password" }
  end
end
```

追加の属性を追加した子ファクトリーを作成する代わりに：

```ruby
FactoryBot.define do
  factory :application_user, parent: :user do
    full_name { "Jane Doe" }
    date_of_birth { 21.years.ago }
    gender { "Female" }
    health { 90 }
  end
end
```

代わりにそのファクトリーを変更できます。

```ruby
FactoryBot.modify do
  factory :user do
    full_name { "Jane Doe" }
    date_of_birth { 21.years.ago }
    gender { "Female" }
    health { 90 }
  end
end
```

ファクトリーを変更するときは、コールバックを除き、必要な属性を変更できます。

ファクトリーに対して異なる動作をするため、`FactoryBot.modify`は` FactoryBot.define`ブロックの外側で呼び出す必要があります。

警告：変更できるのは、ファクトリー（シーケンスまたはトレイトではない）とコールバックのみです（通常の場合と同じように*複合されます）。
だから、もし変更しているファクトリーは `after(:create)`コールバックを定義し、`after(:create)`を定義してもそれはオーバーライドされません。最初のコールバックの後に実行されます。

## 複数のレコードの作成または作成（Building or Creating Multiple Records）

場合によっては、ファクトリーの複数のインスタンスを一度に作成または構築する必要があります。

```ruby
built_users   = build_list(:user, 25)
created_users = create_list(:user, 25)
```

これらのメソッドは、特定の量のファクトリーを構築または作成し、それらを配列として返します。
各ファクトリーの属性を設定するには、通常どおりハッシュを渡すことができます。

```ruby
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
```

`build_stubbed_list`は完全にスタブアウトされたインスタンスを提供します。

```ruby
stubbed_users = build_stubbed_list(:user, 25) # スタブユーザーの配列
```

一度に2つのレコードを作成するための `* _pair`メソッドのセットもあります。

```ruby
built_users   = build_pair(:user) # 2人のビルドされたユーザーの配列
created_users = create_pair(:user) # 作成された2人のユーザーの配列
```

複数の属性ハッシュが必要な場合、 `attributes_for_list`はそれらを生成します：

```ruby
users_attrs = attributes_for_list(:user, 25) # 属性ハッシュの配列
```

## リンティングファクトリー（Linting Factories）

`factory_bot`は、既知のファクトリーのリントを許可します：

```ruby
FactoryBot.lint
```

`FactoryBot.lint`は各ファクトリーを作成し、発生した例外をキャッチします
作成プロセス中。 `FactoryBot :: InvalidFactoryError`は
可能性のある工場の工場（および対応する例外）のリスト作成されません。

`FactoryBot.lint`の推奨される使用法は、タスクで実行することです。
テストスイートが実行される前、`before(:suite)`で実行します。
※：あなたのテストの単一のテストを実行するときパフォーマンスに悪影響を及ぼします。

Rakeタスクの例：

```ruby
# lib/tasks/factory_bot.rake
namespace :factory_bot do
  desc "すべてのFactoryBotファクトリーが有効であることを確認"
  task lint: :environment do
    if Rails.env.test?    
      conn = ActiveRecord::Base.connection
      conn.transaction do
        FactoryBot.lint
        raise ActiveRecord::Rollback
      end
    else
      system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
      fail if $?.exitstatus.nonzero?
    end
  end
end
```

上記の例は、SQLトランザクションとロールバックを使用して、データベースをクリーンなままにします。

リントしたいファクトリーのみを渡すことで、ファクトリーを選択的にリントできます。

```ruby
factories_to_lint = FactoryBot.factories.reject do |factory|
  factory.name =~ /^old_/
end

FactoryBot.lint factories_to_lint
```

これにより、「old_」という接頭辞が付いていないすべてのファクトリーがリントされます。

特性もリントできます。このオプションは、それぞれがファクトリーのすべての特性は、有効なオブジェクトを独自に生成します。
これは、`traits: true`を` lint`メソッドに渡すことで有効になります：

```ruby
FactoryBot.lint traits: true
```

これは、他の引数と組み合わせることもできます。

```ruby
FactoryBot.lint factories_to_lint, traits: true
```

リンティングに使用される戦略を指定することもできます。

```ruby
FactoryBot.lint strategy: :build
```

詳細なリンティングには、デバッグに役立つ各エラーの完全なバックトレースが含まれます。

```ruby
FactoryBot.lint verbose: true
```

カスタム構造（Custom Construction）
-------------------

`factory_bot`を使用して、いくつかの属性を持つオブジェクトを構築する場合
`initialize`に渡されるか、単純な以外の何かをしたい場合、ビルドクラスで `new`を呼び出すと、デフォルトの動作をオーバーライドできます。
ファクトリーで「initialize_with」を定義します。

例：

```ruby
class User
  attr_accessor :name, :email

  def initialize(name)
    @name = name
  end
end

# factories.rb
sequence(:email) { |n| "person#{n}@example.com" }

factory :user do
  name { "Jane Doe" }
  email

  initialize_with { new(name) }
end

build(:user).name # Jane Doe
```

`factory_bot`はActiveRecordをそのまま使用できるように書かれていますが、Rubyクラスでも動作します。 ActiveRecordとの最大限の互換性のために、デフォルトの`initialize`は、ビルドクラスで `new`を呼び出すことですべてのインスタンスをビルドします。
引数なし。
次に、属性ライターメソッドを呼び出して、すべての属性値。
ActiveRecordではうまくいきますが、実際にはうまくいきません
他のほぼすべてのRubyクラスで動作します。

次の目的で初期化子をオーバーライドできます。

* `initialize`への引数を必要とする非ActiveRecordオブジェクトを構築する
*「new」以外のメソッドを使用してインスタンスをインスタンス化する
*ビルド後にインスタンスを飾るなどのクレイジーなことをする

`initialize_with`を使用する場合、クラス自体を宣言する必要はありません。
「new」の呼び出し：ただし、呼び出したい他のクラスメソッドは。クラスで明示的に呼び出されます。

例えば：

```ruby
factory :user do
  name { "John Doe" }

  initialize_with { User.build_with_name(name) }
end
```

また、`attributes`を呼び出すことにより「initialize_with」ブロック内のすべてのパブリック属性にアクセスできます

```ruby
factory :user do
  transient do
    comments_count { 5 }
  end

  name "John Doe"

  initialize_with { new(attributes) }
end
```

これにより、すべての属性のハッシュが作成され、「new」に渡されます。
一時的な属性が含まれますが、ファクトリーで定義されている他のすべては適応されません。

`FactoryBot.define`に含めることで、すべてのファクトリーに対して「initialize_with」を定義できます。

```ruby
FactoryBot.define do
  initialize_with { new("Awesome first argument") }
end
```

「initialize_with」を使用する場合、「initialize_with」内からアクセスされる属性
ブロックはコンストラクターでのみ割り当てられます。

```ruby
FactoryBot.define do
  factory :user do
    initialize_with { new(name) }

    name { 'value' }
  end
end

build(:user)
# runs
User.new('value')
```


## オブジェクトを永続化するカスタムメソッド（Custom Methods to Persist Objects）

デフォルトでは、レコードを作成すると、インスタンスで「保存」が呼び出されます。それは常に理想的であるとは限らない可能性があります。

ファクトリーの `to_create`

```ruby
factory :different_orm_model do
  to_create { |instance| instance.persist! }
end
```

作成時に永続化メソッドを完全に無効にするには、 `skip_create`を実行します

そのファクトリーの場合：

```ruby
factory :user_without_database do
  skip_create
end
```

すべてのファクトリーで「to_create」をオーバーライドするには、

`FactoryBot.define`ブロック：

```ruby
FactoryBot.define do
  to_create { |instance| instance.persist! }

  factory :user do
    name { "John Doe" }
  end
end
```

ActiveSupport Instrumentation
-----------------------------

作成された工場（および構築戦略）を追跡するために、
「`ActiveSupport::Notifications`」は、サブスクライブする方法を提供するために含まれています
1つの例は、に基づいてファクトリーを追跡することです
実行時間のしきい値。

```ruby
ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload|
  execution_time_in_seconds = finish - start

  if execution_time_in_seconds >= 0.5
    $stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}"
  end
end
```

別の例では、すべての工場とその使用方法を追跡します。

RSpecを使用している場合、それは追加するのと同じくらい簡単です。

`before（：suite）`および `after（：suite）`：

```ruby
factory_bot_results = {}
config.before(:suite) do
  ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload|
    factory_name = payload[:name]
    strategy_name = payload[:strategy]
    factory_bot_results[factory_name] ||= {}
    factory_bot_results[factory_name][strategy_name] ||= 0
    factory_bot_results[factory_name][strategy_name] += 1
  end
end

config.after(:suite) do
  puts factory_bot_results
end
```

RailsプリローダーとRSpec
--------------------------

`spring`や` zeus`などのRailsプリローダーでRSpecを実行すると、
ファクトリーを作成するときに `ActiveRecord::AssociationTypeMismatch`エラーが発生します。

以下のように、関連付けを使用します。

```ruby
FactoryBot.define do
  factory :united_states, class: Location do
    name { 'United States' }
    association :location_group, factory: :north_america
  end

  factory :north_america, class: LocationGroup do
    name { 'North America' }
  end
end
```

エラーは、テストスイートの実行中に発生します。

```shell
Failure/Error: united_states = create(:united_states)
ActiveRecord::AssociationTypeMismatch:
  LocationGroup(#70251250797320) expected, got LocationGroup(#70251200725840)
```

2つの可能な解決策は、プリローダーなしでスイートを実行するか、
または次のように、 `FactoryBot.reload`をRSpec設定に追加します。

```ruby
RSpec.configure do |config|
  config.before(:suite) { FactoryBot.reload }
end
```


参考：
[factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails)
[factory_bot](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md)