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を作成した。
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
# 省略
ファクトリ
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も変えた。
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
# 省略
factory :user do
name { "user" }
sequence(:acount_id) { |n| "www#{n}" }
trait :duplicate_acount_id_user do
name { "user2" }
acount_id { "wow" }
end
通った。
トレイトやシークエンスを使う必要もないと思うが、練習のために使ってみた。