FactoryBotでインスタンス生成時、属性値のFakerが更新されないときの対処法

# ざっくりまとめ
FactoryBotで、属性値にFakerを用いる場合、*Dynamic Attributes* という書き方を用いる。

つまりはこんな感じ。

```ruby
FactoryBot.define do
  factory :user, class: User do
    email         {Faker::Internet.email} //ここを｛｝で囲んでいる書き方がDynamic Attributes
    name          {Faker::Name.name}      //上に同じ
  end
end
```
# 背景
## はじめに
Ruby on Railsのアプリケーションのテストコードを作成していました。Userモデルを生成し、FactoryBotとFakerでUserモデルのインスタンスを生成するためのファイルを生成しました。

app/models/user.rb

```ruby
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  
  validates :name, :email, presence: true, uniqueness: true
end
```

spec/factories/users.rb

```ruby
FactoryBot.define do
  factory :user, class: User do
    email         Faker::Internet.email
    name          Faker::Name.name

    (中略)
  end
end
```

## コードを動かしてみる
これでテストコードでuserインスタンスを生成するごとに、Fakerのおかげでインスタンス変数にランダムな文字列が割りあてられるはず....!!!

試しにrailsコンソールで、Factoryを用いてuserインスタンスを作成してみると、
（必要そうな部分のみ抜粋）

```
[1] pry(main)> FactoryBot.create(:user)
   (0.2ms)  BEGIN
  SQL (123.3ms)  INSERT INTO `users` (`email`, `encrypted_password`, `name`,  `created_at`, `updated_at`) VALUES ('torrance@gorczanyjohnson.com', '$2a$11$kppvm7CCDvuj7JJEVPH8nugI1RwkYi9ElyuD/jKgLuQjQU9xtDoSe', 'Shad Kemmer','2018-01-03 17:54:21', '2018-01-03 17:54:21')
   (40.1ms)  COMMIT
=> #<User id: 55, email: "torrance@gorczanyjohnson.com", name: "Shad Kemmer",  created_at: "2018-01-03 17:54:21", updated_at: "2018-01-03 17:54:21">
```
いい感じです。もう一回同じuserインスタンスを生成してみます。

```
[2] pry(main)> FactoryBot.create(:user)
   (0.3ms)  BEGIN
   (11.8ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken, Name has already been taken
```
バリデーションエラー...。 Userモデルでemailとnameカラムにかけたユニーク制約で引っかかっているようです。つまり、1回目に作成したインスタンスと、2回目に作成したインスタンスのemailとnameの値が同じになっているようです。

nameとemailの値はFakerで定義しているので、インスタンス生成ごとに異なる値で定義されてほしいですね。

# 原因を探る
## FactorybotのReadmeを読んでみた
FactoryBotのReadme（Getting Started）を読んで見ると、こんな項目があった。

> ### Dynamic Attributes
> Most factory attributes can be added using static values that are evaluated when the factory is defined, but some attributes (such as associations and other attributes that must be dynamically generated) will need values assigned each time an instance is generated. These "dynamic" attributes can be added by passing a block instead of a parameter:

> ```ruby
factory :user do
  # ...
  activation_code { User.generate_activation_code }
  date_of_birth   { 21.years.ago }
end
```

要約すると、
アソシエーションなどのコードをFactoryBotの属性値とする際、波括弧｛｝で属性値となる部分を囲むと、dynamic attributes として判断される。 また、dynamic attributesとして判断された属性値は、インスタンスを生成するたびに定義されるということです。

## ファイルを修正する
FactoryBotのReadmeにある通り、Fakerで定義しているemailとnameの値をDynamic Attributesとして定義します。

spec/factories/users.rb

```ruby
FactoryBot.define do
  factory :user, class: User do
    email         { Faker::Internet.email }
    name          { Faker::Name.name }

    (中略)
  end
end
```

## コードを動かしてみる

再度railsコンソールで、Factoryを用いてuserインスタンスを作成してみると、

```
[1] pry(main)> FactoryBot.create(:user)
   (0.2ms)  BEGIN
  SQL (45.5ms)  INSERT INTO `users` (`email`, `encrypted_password`, `name`, `profile`, `works`, `avatar`, `occupation`, `created_at`, `updated_at`) VALUES ('leie@will.net', '$2a$11$9.0f13f.z.Nq.y1n3bGA7exHcDwCOU6I5lXOM3ZqkpSWkLZFAA0GS', 'Cyril Hammes', '2018-01-03 18:34:42', '2018-01-03 18:34:42')
   (11.7ms)  COMMIT
=> #<User id: 56, email: "leie@will.net", name: "Cyril Hammes", created_at: "2018-01-03 18:34:42", updated_at: "2018-01-03 18:34:42">
```

```
[2] pry(main)> FactoryBot.create(:user)
   (0.2ms)  BEGIN
  SQL (11.6ms)  INSERT INTO `users` (`email`, `encrypted_password`, `name`, `created_at`, `updated_at`) VALUES ('neil.kreiger@lind.com', '$2a$11$SNdq09WdnRplpWe4Uxew.eU8jS/18JE5JKnnSKYCsxPq7.F8XqQ4y', 'Elsa Runolfsson', '2018-01-03 18:34:44', '2018-01-03 18:34:44')
   (1.5ms)  COMMIT
=> #<User id: 57, email: "neil.kreiger@lind.com", name: "Elsa Runolfsson",  created_at: "2018-01-03 18:34:44", updated_at: "2018-01-03 18:34:44">
```
1回目と2回目のuserインスタンスを比較すると、emailとnameの値が異なっていることがわかります。Fakerが正常に動作していますね!!

### ちなみに

FactoryBot Readmeの続きを読んでみると、こんなことも。
>### Dynamic Attributes
> Because of the block syntax in Ruby, defining attributes as Hashes (for serialized/JSON columns, for example) requires two sets of curly brackets:

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

factoryの属性値としてハッシュを用いる場合、波括弧｛｝2セットが必要ですよ! とのことでした。


# 動作環境
- ruby 2.4.0
- rails 5.0.1
- faker 1.8.7
- factory_bot_rails 4.8.2

# 参考
[FactoryBot Readme]
(https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md)