はじめに
皆様、こんにちは!
佐久間まゆちゃんのプロデューサーの@hiroki_tanakaです。
私は現在、Railsを使用してメール送信機能を持つシステムを構築しているのですが、
Railsの持つActionMailer機能のRSpecでのテスト実装を少し調べたので紹介させて下さい。
記事の流れとしては、最初に利用環境とActionMailerを使うための下準備に触れ、
その後に実際のActionMailer側のコードとRSpecのコードを記載致します。
利用環境
Ruby 2.6.6
Rails 6.0.2
RSpec 3.9.1
下準備
今回はmailgunをESP(Email Service Provider)として、メール送信を行います。
そのため、mailgunで行った設定をdevelopment.rb
に記載をして、rails側でmailgunを使用出来るように設定をします。
ActionMailerのRSpecテストだけ行う場合は本設定は不要ですが、メール送信機能を通常のアプリケーション側から実行する場合は必要です。
config.action_mailer.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
user_name: 'address@domain',
password: 'password',
domain: 'domain',
address: 'smtp.mailgun.org',
port: 587,
authentication: :plain,
enable_starttls_auto: true
}
次に、mailerとspecファイルを作成します。
下記のrails generateコマンドを実行すれば、TestMailer
クラスと紐づくSpecファイルが作成されます。
$ rails g mailer TestMailer
最後に、config/environments/test.rb
を開き、
下記のように設定されていることを確認します。
下記のように設定されていると、ActionMailerのテストで実際にメールが送信されずにテストモードで動作します。
config.action_mailer.delivery_method = :test
ActionMailer
Mailerクラスにはメール送信のためのコードを記載していきます。
class TestMailer < ApplicationMailer
def send_mail
mail_info = {
to: 'hoge.from@test.com',
from: 'fuga.to@test.com',
from_display_name: 'ほげ商事',
subject: 'ほげ商事の田中太郎です',
body: '本メールはほげ商事の田中太郎からのテストメールです。'
}
from = Mail::Address.new mail_info['from']
from.display_name = mail_info['from_display_name']
mail(subject: mail_info['subject'], from: from.format, to: mail_info['to']) do |format|
format.text { render plain: mail_info['body'] }
end
end
end
TestMailer
はApplicationMailer
というActionMailerの基底クラスを継承しています。
その中にメール送信を行うsend_mail
という関数を定義します。
mail_info
というハッシュを作成し、メール送信に必要な情報を定義します。
(from_display_name
というのはメール受診時に送信者欄に表示される名前です。)
次に、mail gemのMail::Address.new
を使用して、from
及びdisplay_name
をActionMailerで使用する型に変換します。
そして、最後のmail
メソッドでsubject
(メールタイトル)・from
(送信元)・to
(送信先)を指定します。
format
はHTMLメール形式かプレーンテキスト形式か指定できます。
今回はプレーンテキスト形式で送信するためrender plain
として、本文を設定しています。
以上でActionMailer側のコードは完成です。
下記のコマンド実行すれば、メール送信がされます。
$ rails c
[1] pry(main)> TestMailer.send_mail.deliver
RSpec
TestMailer
のテストコードを記載していきます。
require 'rails_helper'
describe TestMailer do
describe '#send_mail' do
subject(:mail) do
described_class.send_mail.deliver_now
ActionMailer::Base.deliveries.last
end
context 'when send_mail' do
it { expect(mail.from.first).to eq('hoge.from@test.com') }
it { expect(mail.to.first).to eq('fuga.to@test.com') }
it { expect(mail.subject).to eq('ほげ商事の田中太郎です') }
it { expect(mail.body).to match(/本メールはほげ商事の田中太郎からのテストメールです。/) }
end
end
end
subject(:mail)
で最初のdescribeで宣言されたクラス(今回はTestMailer
)のsend_mail
を実行することを定義しています。
次のActionMailer::Base.deliveries.last
は最後に送信されたメールの情報を返却します。
そのため、このmail
はメール送信処理を行い、値として送信したメールの情報を保持します。
context内のitでは、まずsubjectで定義したmail
を呼び出し、メールの送信元/送信先のfrom/toをチェックしています。
mail.to.first
やmail.from.first
としている理由は、mail
の戻り値内のfrom/toの情報が
[hoge.from@test.com]
/[fuga.to@test.com]
と配列になっているためです。
(これはメールの送信元・送信先は複数存在することがあるためです。)
その後、メールタイトルであるsubjectと本文であるbodyを比較します。
bodyは本文以外の情報(encodingなど)も含まれるため、eq matcher
ではなくmatch matcher
を使用します。
以上でRSpecの作成は完了です。コマンドラインからRSpecを実行すれば、正常にパスします。
これでActionMailerと紐づくRSpecのどちらも完成しました。
おわりに
実装を始める前はメール送信機能やそのRSpecが難しく感じましたが、
実際に書いてみると非常に簡単かつ便利にメール送信が出来ました。