0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FactoryBotでのsequenceでつまずいたのでメモ

Posted at

sequenceとは

ユニークなデータを生成するためのメソッド。

モデルで一意性のバリデーションを定義指定た場合、

user1 = FactoryBot.create(:user)

user2 = FactoryBot.create(:user)

はuserの属性が一意性のバリデーションに引っかかってしまい、生成することができない。

だが、ファクトリで

# emailにバリデーションがかかっている場合
sequence(:email) { |n| "exmple#{n}@example.com" }

こうすることで、このファクトリが呼び出されるたびに、nの部分に数字が一つずつ増えて入るため、一意性が保たれる。
そのため、生成することができる。

user1 = FactoryBot.create(:user)
# emailは"example1@example.com"

user2 = FactoryBot.create(:user)
# emailはexample2@example.com"

つまずいたこと

モデルスペックで一意性を検証するitを作成した。

user_spec.rb

let(:user) { FactoryBot.build(:user) }
let(:duplicate_acount_id_user) { FactoryBot.build(:user, :duplicate_acount_id_user) }
# 省略

it "acount_idは一意でなければ無効であること" do
    user.save
    expect(duplicate_acount_id_user).to_not be_valid
  end

# 省略

ファクトリ

user.rb
factory :user do
   name { "user" }
   sequence(:acount_id) { |n| "www#{n}" }
   
 trait :duplicate_acount_id_user do
       name { "user2" }
       acount_id { "www1" }
   end

モデルスペック内ではuser.saveでuserが呼び出されて、sequenceによりそのuserのacount_idは"www1"になると思っていたので、トレイトの方は、acount_idを同じにするために"www1"と設定した。

だが、 bin/rspecで確かめると

# エラー文
Failure/Error: expect(duplicate_acount_id_user).to_not be_valid

が出た。

これにつまずいてしまった。

確かめてみたこと

focusをつけて単体で実行してみたら通った。

つまり、単体では最初にsaveされたacount_idが"www1"にちゃんとなっているということ。また、モデルのバリデーションも機能してるということ。

なので、全体で実行したらどのようになってるのかを見てみた。

it "acount_idは一意でなければ無効であること" do
    user.save
    # expect(user)にすることする事で、エラーになり、エラー文でuserの属性の値を見ることができる
    expect(user).to_not be_valid
  end

実行してみたら、acount_idが、"www15"になってた。

ということはuser.saveで読み込まれたuserのは"www14"だということである。(多分)

なぜつまずいたか

1つのexampleが終わるたびにデータが削除されるのと同じように、ファクトリのデータもそのつどリセットされるものだと思っていたため。どうやらsequenceのカウントは継続されているっぽい。

解決法

saveする前にacount_idを指定。
ファクトリのトレイトの方のacount_idも変えた。

user_spec.rb

let(:user) { FactoryBot.build(:user) }
let(:duplicate_acount_id_user) { FactoryBot.build(:user, :duplicate_acount_id_user) }
# 省略

it "acount_idは一意でなければ無効であること" do
    user.acount_id = "wow"
    user.save
    expect(duplicate_acount_id_user).to_not be_valid
  end

# 省略
user.rb
factory :user do
    name { "user" }
    sequence(:acount_id) { |n| "www#{n}" }
    
  trait :duplicate_acount_id_user do
        name { "user2" }
        acount_id { "wow" }
    end

通った。

トレイトやシークエンスを使う必要もないと思うが、練習のために使ってみた。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?