はじめに
この記事は2021年11月にまとめていた「細かいつまずいたことをメモしておく(11月編)をそれぞれ投稿した内容になります
解決方法が最新でない可能性もありますのでご了承ください
やりたいこと
以下のようなモデルのメソッドをテストします
ランダムな文字列をlogin_idにしてユーザーでそのログインIDを利用している人がいるかを検索
- いなければlogin_idを返す
- いた場合は再度unique_login_idを呼び出す
test.rb
def unique_login_id
login_id = SecureRandom.alphanumeric(6).downcase
return login_id if User.find_by(login_id: login_id).blank?
unique_login_id
end
ここで2のすでにログインIDが利用されている場合のテストに困りました
test_spec.rb
describe 'unique_login_id_assign' do
subject(:result) { test.send(:unique_login_id) }
let(:test) { create(:test) }
let(:login_id) { 'test_id' }
before do
user = create(:user)
user.login_id = login_id
user.save
end
context '作成した共通IDがログインIDと重複しているとき' do
it 'ユニークな共通IDを返すこと' do
allow(SecureRandom).to receive(:alphanumeric).and_return(login_id)
expect(result).to eq login_id
end
end
この方法だとallowが常に同じものを返し続けるためループし続けてしまいます
1回目はログインIDが重複して2回目に重複しないIDをlogin_idに入れたいです
解決方法
以下の記事を見つけました
この記事によるとand_returnで複数の値を返せるようになるとのこと
test2_spec.rb
user = User.new
allow(user).to receive(:name).and_return('たろう', 'じろう', 'さぶろう')
user.name # たろう
user.name # じろう
user.name # さぶろう
user.name # さぶろう
これをうまく利用して以下のテストに変更しました
test_spec.rb
describe 'unique_login_id_assign' do
subject(:result) { test.send(:unique_login_id) }
let(:test) { create(:test) }
let(:login_id) { 'test_id' }
before do
user = create(:user)
user.login_id = login_id
user.save
end
context '作成した共通IDがログインIDと重複しているとき' do
it 'ユニークな共通IDを返すこと' do
allow(SecureRandom).to receive(:alphanumeric).and_return(login_id, 'unique_login_id')
expect(result).to eq login_id
end
end
ちなみにモデルSpecのプライベートメソッドはsend
を使うことで呼び出せます