動作は問題ないのにrspecのテストを通過できない
はじめに
エンジニア転職を目指しRuby on Railsを中心に学習中の初学者です。
備忘録として、躓いたことやケアレスミスも含め投稿します!誤っている箇所などありましたらご指摘いただけると幸いです
対象読者
- 初学者
記事概要
エラー要因が不明でchatGPT
を使って解決したケアレスミスを戒めとして残します。
#命令書:
あなたはRuby on Railsのエキスパートです。
以下の制約条件と入力文をもとに、最高の結果を出力してください。
#制約条件:
・必ず初学者が理解できるように細かく解説してください。
・
#入力文:
・下記のrspecファイルと実行時のエラーについて解説をお願いします。
・会員登録時にメールアドレスが一意であることをテストしたいテストコードで下記テスト未通過が発生しています。
#user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
it 'ニックネーム、メールがあり、パスワードは3文字以上であれば有効であること' do
user = build(:user)
expect(user).to be_valid
end
it 'メールはユニークであること' do
user1 = create(:user)
user2 = build(:user)
user2.email = user1.email
user2.valid?
expect(user2.errors[:email]).to include('はすでに存在します')
end
it 'ニックネーム、メールアドレスが必須項目であること' do
user = build(:user)
user.name = nil
user.email = nil
user.valid?
expect(user.errors[:name]).to include('を入力してください')
expect(user.errors[:email]).to include('を入力してください')
end
it 'ニックネームは255文字以内であること' do
user = build(:user)
user.name = 'a' * 256
end
end
ターミナルのテスト結果
Failures:
1) User メールはユニークであること
Failure/Error: expect(user2.errors[:email]).to include('はすでに存在します')
expected ["メールアドレスはすでに使用されています"] to include "はすでに存在します"
# ./spec/models/user_spec.rb:14:in `block (2 levels) in <top (required)>'
Finished in 2.38 seconds (files took 10.55 seconds to load)
4 examples, 1 failure
ChatGPT:
この問題は、RSpecのテストが期待するエラーメッセージと、実際に得られたエラーメッセージが一致していないために発生しています。具体的には、テストコードで user2.errors[:email]
に期待されているエラーメッセージが "はすでに存在します"
となっていますが、実際には "メールアドレスはすでに使用されています"
というメッセージが返されています。
解決策
エラーメッセージが期待される内容と異なることが原因ですので、2つの解決方法があります。
1. テストで期待するエラーメッセージを修正する
現在のテストコードでは expect(user2.errors[:email]).to include('はすでに存在します')
としていますが、実際に返されるエラーメッセージは "メールアドレスはすでに使用されています"
ですので、テストコードを次のように修正します。
ruby
コードをコピーする
it 'メールはユニークであること' do
user1 = create(:user)
user2 = build(:user)
user2.email = user1.email
user2.valid?
expect(user2.errors[:email]).to include('メールアドレスはすでに使用されています')
end
これにより、エラーメッセージが正しく一致し、テストは通るようになります。
2. カスタムのエラーメッセージを設定する
もし、アプリケーション全体でエラーメッセージを "はすでに存在します"
に統一したい場合は、config/locales/ja.yml
などのロケールファイルでカスタムのエラーメッセージを定義することができます。例えば、以下のように設定します。
config/locales/ja.yml
:
yaml
コードをコピーする
ja:
activerecord:
errors:
models:
user:
attributes:
email:
taken: 'はすでに存在します'
こうすることで、User
モデルの email
が重複した場合に はすでに存在します
というメッセージが返されるようになります。
エラーの原因の解説
-
user1
というユーザーをcreate
メソッドでデータベースに保存しています。このとき、user1
のメールアドレスはデータベースに登録されます。 -
user2
というユーザーをbuild
メソッドで作成しますが、このユーザーはまだデータベースに保存されていません。次にuser2.email = user1.email
とすることで、user1
と同じメールアドレスを持つユーザーを作成しています。 -
user2.valid?
でバリデーションを実行した際、email
がすでに存在するため、user2
のerrors
に「メールアドレスはすでに使用されています」というエラーが追加されます。 - テストでは
user2.errors[:email]
の内容が"はすでに存在します"
であることを期待していますが、実際には"メールアドレスはすでに使用されています"
となっており、これがエラーの原因です。
テストコード全体の確認
ruby
コードをコピーする
require 'rails_helper'
RSpec.describe User, type: :model do
it 'ニックネーム、メールがあり、パスワードは3文字以上であれば有効であること' do
user = build(:user)
expect(user).to be_valid
end
it 'メールはユニークであること' do
user1 = create(:user)
user2 = build(:user)
user2.email = user1.email
user2.valid?
expect(user2.errors[:email]).to include('メールアドレスはすでに使用されています') # 修正済み
end
it 'ニックネーム、メールアドレスが必須項目であること' do
user = build(:user)
user.name = nil
user.email = nil
user.valid?
expect(user.errors[:name]).to include('を入力してください')
expect(user.errors[:email]).to include('を入力してください')
end
it 'ニックネームは255文字以内であること' do
user = build(:user)
user.name = 'a' * 256
user.valid?
expect(user.errors[:name]).to include('は255文字以内で入力してください')
end
end
これで、エラーメッセージの不一致によるテスト失敗が解消されるはずです。
expect(user2.errors[:email]).to include('メールアドレスはすでに使用されています') # 修正済み
まさか上記の'メールアドレスはすでに使用されています'の
エラーメッセージの相違だけでテストに通らなくなるのは盲点でした。
Discussion