6
Help us understand the problem. What are the problem?

posted at

updated at

Rails 6.1.5で uninitialized constant Mail::TestMailer というエラーが出る場合の対処法

Railsでメールを送信しようとすると以下のようなエラーが出る場合があります。

NameError (uninitialized constant Mail::TestMailer

      delegate :deliveries, :deliveries=, to: Mail::TestMailer
                                                  ^^^^^^^^^^^^):
  
app/mailers/application_mailer.rb:1:in `<main>'
app/mailers/user_mailer.rb:1:in `<main>'
app/controllers/home_controller.rb:3:in `index'

Screen Shot 2022-03-30 at 7.41.05.png

僕が調べる限り、以下の条件が揃ったときに発生するようです。

  • Rails 6.1.5を使用(これ以外のバージョンだと発生しない)
  • Ruby 3.1系を使用(Ruby 3.0だと発生しない)
  • メール送信を実行しようとする

原因と解決方法

どうやらRuby 3.1でnet-smtpが外部gemになったことに起因しているようです。

The following default gems are now bundled gems.

  • net-ftp 0.1.3
  • net-imap 0.2.2
  • net-pop 0.1.1
  • net-smtp 0.3.1
  • matrix 0.4.2
  • prime 0.1.2
  • debug 1.4.0

https://github.com/ruby/ruby/blob/v3_1_0/NEWS.md

Gemfileに"net-smtp"を追加してbundle install&サーバーを再起動するとこのエラーは解消します。

Gemfile
gem 'net-smtp'
# ついでに以下の2つも追加しておく方が良さそう
gem 'net-imap'
gem 'net-pop'

もう少し詳しく

Rails 6.1.5よりも前(Rails 6.1.4.7など)では、"cannot load such file -- net/smtp"というエラーメッセージが表示されるため、net-smtp gemを追加すれば直ることが予想しやすいです。

LoadError (cannot load such file -- net/smtp):
  
app/mailers/application_mailer.rb:1:in `<main>'
app/mailers/user_mailer.rb:1:in `<main>'
app/controllers/home_controller.rb:3:in `index'

Screen Shot 2022-03-30 at 8.14.54.png

また、以下のPRでnet-smtpが自動的にインストールされるようになったため、Rails 7.0.1以降ではこの問題は発生しません。

Rails 6.1.5でだけ、このエラーが発生する理由

では、なぜRails 6.1.5で"uninitialized constant Mail::TestMailer"というエラーが出るのかというと、Rails 6.1.5の例外処理に問題があるからです。

# frozen_string_literal: true

begin
  require "mail"
rescue LoadError => error
  if error.message.match?(/net-smtp/)
    $stderr.puts "You don't have net-smtp installed in your application. Please add it to your Gemfile and run bundle install"
    raise
  end
end

error.messageの中身は"cannot load such file -- net/smtp"になっているため、上記コードの正規表現は、

-if error.message.match?(/net-smtp/)
+if error.message.match?(/net\/smtp/)

としなければなりません。

このif文が真にならないため、net-smtpのLoadErrorが発生しても完全に無視され、mail gemがloadされないままサーバー起動するため、Mail::TestMailerを参照するタイミングでエラーが発生します。

この問題は以下のPRで修正されているため、Rails 6.1系の次回リリースで修正されるものと思われます。

Rails 6.1.6がリリースされ、 "You don't have net-smtp installed in your application. Please add it to your Gemfile and run bundle install" というエラーメッセージが意図通り出力されるようになりました。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
6
Help us understand the problem. What are the problem?