LoginSignup
4
2

More than 5 years have passed since last update.

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

Posted at

ざっくりまとめ

FactoryBotで、属性値にFakerを用いる場合、Dynamic Attributes という書き方を用いる。

つまりはこんな感じ。

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

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  validates :name, :email, presence: true, uniqueness: true
end

spec/factories/users.rb

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:

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

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:

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

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2