[訂正] FactoryBot(FactoryGirl) の before/after で stack level too deep する問題への対処

# 訂正

オモクソ書き方を間違えてた

元々の `stack level too deep` が出たコード

```ruby
FactoryBot.define do
  factory :product do
    name "MyString"
    maker nil
  end

  before(:create) do |product|
    product.maker = create(:maker)
  end
end
```

正しくは下記のように書きます

```ruby
FactoryBot.define do
  factory :product do
    name "MyString"
    maker nil

    before(:create) do |product|
      product.maker = create(:maker)
    end
  end
end
```

以上！

# 以下元記事

## 現象

`Maker` model と、 `Product` model があるような状態で、spec を書く時こんな感じで FactoryBot を書いててたら `stack level too deep` が出た。

```ruby
FactoryBot.define do
  factory :product do
    name "MyString"
    maker nil
  end

  before(:create) do |product|
    product.maker = create(:maker)
  end
end
```
```shell-session
$ bundle exec rspec -fd spec/models/product_spec.rb

Product
  example at ./spec/models/product_spec.rb:4 (FAILED - 1)
  example at ./spec/models/product_spec.rb:5 (FAILED - 2)

Failures:

  1) Product 
     Failure/Error: product.maker = create(:maker)
     
     SystemStackError:
       stack level too deep
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
     # ./spec/factories/products.rb:19:in `block (2 levels) in <main>'
...
```

## 原因

理由は単純で `before(:create)` 内で、 `create(:maker)` を実行すると、`before(:create)` が再度実行されて永久ループに入る。

~~`:product` のイベントなのに、`create(:maker)` でも実行されちゃう。~~

**訂正:** `factory :product` block 内に書くべきなのが block 外に書いている為、 `FactoryBot.define` block で実行されてしまていた為、全ての `create` イベントで実行されてしまっていた。

## 対処法

対処法は下記の通り。

```ruby
FactoryBot.define do
  factory :product do
    name "MyString"
    maker { build(:maker) }
  end
end
```

## 何も書かないのが吉？

ちなみに、下記のように何も書かずに `maker` だけ宣言すると勝手に`create(:maker)`してくれる。

```ruby
# spec/factories/products.rb
FactoryBot.define do
  factory :product do
    name "MyString"
    maker
  end
end
```
```ruby
# spec/models/product_spec.rb
RSpec.describe Product, type: :model do
  it { expect(create(:product)).to be_a(described_class) } #pass
  it { expect(create(:product).maker).to be_a(Maker) } #pass
end
```
```shell-session
$ bundle exec rspec -fd spec/models/product_spec.rb

Product
  should be a kind of Product(id: integer, name: string, maker_id: integer, created_at: datetime, updated_at: datetime)
  should be a kind of Maker(id: integer, name: string, created_at: datetime, updated_at: datetime)

Finished in 0.03272 seconds (files took 1.64 seconds to load)
2 examples, 0 failures
```

## Factories 定義で create しない方がいい？

ただ、この時 `Maker` を `create` しちゃうので、最初の例のように `before(:create)` を使って何かしようとすると、`before(:create)` は２回実行される。

```ruby
# spec/factories/products.rb
FactoryBot.define do
  factory :product do
    name "MyString"
    maker
  end

  before(:create) do |product|
    puts "Create!!"
  end
end
```
```ruby
# spec/models/product_spec.rb
RSpec.describe Product, type: :model do
  it { expect(create(:product)).to be_a(described_class) }
  it { expect(create(:product).maker).to be_a(Maker) }
end
```
```shell-session
$ bundle exec rspec spec/models/product_spec.rb
Create!!
Create!!
.Create!!
Create!!
.

Finished in 0.03598 seconds (files took 1.74 seconds to load)
2 examples, 0 failures
```

んー、この仕様は如何なんだろう。
今までこの仕様を把握しないでよく使ってこれたなとびっくりしている。

引数なしの自動 create の影響で、callback が複数回実行されるとか怖いし、なんか対処法はないものか…。

## 参考

- [【解決済み】FactoryGirl の relation の定義の仕方がよくわからない＞＜ \- yoshiori\.github\.io](http://yoshiori.github.io/blog/2013/02/03/factory-girl-relation/)