🔖

動作は問題ないのにrspecのテストを通過できない

2024/12/05に公開

はじめに

エンジニア転職を目指し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 が重複した場合に はすでに存在します というメッセージが返されるようになります。

エラーの原因の解説

  1. user1 というユーザーを create メソッドでデータベースに保存しています。このとき、user1 のメールアドレスはデータベースに登録されます。
  2. user2 というユーザーを build メソッドで作成しますが、このユーザーはまだデータベースに保存されていません。次に user2.email = user1.email とすることで、user1 と同じメールアドレスを持つユーザーを作成しています。
  3. user2.valid? でバリデーションを実行した際、email がすでに存在するため、user2errors に「メールアドレスはすでに使用されています」というエラーが追加されます。
  4. テストでは 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