メール

メール

@adonisjs/mailパッケージを使用して、AdonisJSアプリケーションからメールを送信できます。このメールパッケージは、Nodemailerをベースにしており、以下のような利便性の向上をもたらします。

  • メールメッセージを設定するためのフルエントAPI。
  • より良い組織とテストのために、メールをクラスとして定義する機能。
  • 公式にメンテナンスされているトランスポートの包括的なスイート。smtpsesmailgunsparkpostresendbrevoを含みます。
  • Fakes APIを使用したテストの改善。
  • メールをキューに入れるためのメールメッセンジャー。
  • カレンダーイベントを生成するための機能API。

インストール

次のコマンドを使用してパッケージをインストールし、設定します:

node ace add @adonisjs/mail
# CLIフラグを使用して使用するトランスポートを事前に定義する
node ace add @adonisjs/mail --transports=resend --transports=smtp
  1. 検出されたパッケージマネージャを使用して@adonisjs/mailパッケージをインストールします。

  2. 次のサービスプロバイダとコマンドをadonisrc.tsファイル内に登録します。

    {
    commands: [
    // ...他のコマンド
    () => import('@adonisjs/mail/commands')
    ],
    providers: [
    // ...他のプロバイダ
    () => import('@adonisjs/mail/mail_provider')
    ]
    }
  3. config/mail.tsファイルを作成します。

  4. 選択したメールサービスの環境変数とそのバリデーションを定義します。

設定

メールパッケージの設定はconfig/mail.tsファイルに保存されます。このファイル内で、複数のメールサービスをmailersとして設定できます。

詳細はこちら:Config stub

import env from '#start/env'
import { defineConfig, transports } from '@adonisjs/mail'
const mailConfig = defineConfig({
default: 'smtp',
/**
* "from"プロパティのための静的なアドレス。メールで明示的なfromアドレスが設定されていない場合に使用されます。
*/
from: {
address: '',
name: '',
},
/**
* "reply-to"プロパティのための静的なアドレス。メールで明示的なreplyToアドレスが設定されていない場合に使用されます。
*/
replyTo: {
address: '',
name: '',
},
/**
* mailersオブジェクトは、異なるトランスポートまたは異なるオプションを使用する同じトランスポートを使用して複数のメーラーを設定するために使用できます。
*/
mailers: {
smtp: transports.smtp({
host: env.get('SMTP_HOST'),
port: env.get('SMTP_PORT'),
}),
resend: transports.resend({
key: env.get('RESEND_API_KEY'),
baseUrl: 'https://api.resend.com',
}),
},
})

default

デフォルトでメールを送信するために使用するメーラーの名前。

from

fromプロパティに使用する静的なグローバルアドレス。メールで明示的なfromアドレスが定義されていない場合に使用されます。

replyTo

reply-toプロパティに使用する静的なグローバルアドレス。メールで明示的なreplyToアドレスが定義されていない場合に使用されます。

mailers

mailersオブジェクトは、メールを送信するために使用する1つ以上のメーラーを設定するために使用されます。mail.useメソッドを使用してメーラーを実行時に切り替えることができます。

トランスポートの設定

公式にサポートされているトランスポートが受け入れる設定オプションの完全なリファレンスは次のとおりです。

詳細はこちら:TypeScript types for config object


次の設定オプションは、Mailgunの/messages.mimeAPIエンドポイントに送信されます。

{
mailers: {
mailgun: transports.mailgun({
baseUrl: 'https://api.mailgun.net/v3',
key: env.get('MAILGUN_API_KEY'),
domain: env.get('MAILGUN_DOMAIN'),
/**
* メールの`mail.send`メソッドを呼び出す際にランタイムでオーバーライドできる次のオプション。
*/
oDkim: true,
oTags: ['transactional', 'adonisjs_app'],
oDeliverytime: new Date(2024, 8, 18),
oTestMode: false,
oTracking: false,
oTrackingClick: false,
oTrackingOpens: false,
headers: {
// h:プレフィックス付きヘッダー
},
variables: {
appId: '',
userId: '',
// v:プレフィックス付き変数
}
})
}
}

次の設定オプションは、Nodemailerにそのまま転送されます。そのため、Nodemailerのドキュメントも参照してください。

{
mailers: {
smtp: transports.smtp({
host: env.get('SMTP_HOST'),
port: env.get('SMTP_PORT'),
secure: false,
auth: {
type: 'login',
user: env.get('SMTP_USERNAME'),
pass: env.get('SMTP_PASSWORD')
},
tls: {},
ignoreTLS: false,
requireTLS: false,
pool: false,
maxConnections: 5,
maxMessages: 100,
})
}
}

次の設定オプションは、Nodemailerにそのまま転送されます。そのため、Nodemailerのドキュメントも参照してください。

SESトランスポートを使用するには、@aws-sdk/client-sesパッケージをインストールする必要があります。

{
mailers: {
ses: transports.ses({
/**
* aws sdkに転送されます
*/
apiVersion: '2010-12-01',
region: 'us-east-1',
credentials: {
accessKeyId: env.get('AWS_ACCESS_KEY_ID'),
secretAccessKey: env.get('AWS_SECRET_ACCESS_KEY'),
},
/**
* Nodemailer固有の設定
*/
sendingRate: 10,
maxConnections: 5,
})
}
}

次の設定オプションは、SparkPostの/transmissionsAPIエンドポイントに送信されます。

{
mailers: {
sparkpost: transports.sparkpost({
baseUrl: 'https://api.sparkpost.com/api/v1',
key: env.get('SPARKPOST_API_KEY'),
/**
* メールの`mail.send`メソッドを呼び出す際にランタイムでオーバーライドできる次のオプション。
*/
startTime: new Date(),
openTracking: false,
clickTracking: false,
initialOpen: false,
transactional: true,
sandbox: false,
skipSuppression: false,
ipPool: '',
})
}
}

次の設定オプションは、Resendの/emailsAPIエンドポイントに送信されます。

{
mailers: {
resend: transports.resend({
baseUrl: 'https://api.resend.com',
key: env.get('RESEND_API_KEY'),
/**
* メールの`mail.send`メソッドを呼び出す際にランタイムでオーバーライドできる次のオプション。
*/
tags: [
{
name: 'category',
value: 'confirm_email'
}
]
})
}
}

基本的な例

初期設定が完了したら、mail.sendメソッドを使用してメールを送信できます。メールサービスは、設定ファイルで構成されたdefaultメーラーのシングルトンインスタンスであり、MailManagerクラスのインスタンスです。

mail.sendメソッドは、Messageクラスのインスタンスをコールバックに渡し、メールをconfigファイルで構成されたdefaultメーラーを使用して配信します。

次の例では、新しいユーザーアカウントを作成した後、コントローラーからメールをトリガーしています。

import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
import mail from '@adonisjs/mail/services/main'
export default class UsersController {
async store({ request }: HttpContext) {
/**
* デモンストレーションのためだけです。データをデータベースに保存する前にデータをバリデーションする必要があります。
*/
const user = await User.create(request.all())
await mail.send((message) => {
message
.to(user.email)
.from('info@example.org')
.subject('メールアドレスの確認')
.htmlView('emails/verify_email', { user })
})
}
}

メールのキューイング

メールの送信には時間がかかる場合があるため、キューに入れてバックグラウンドでメールを送信あります。mail.sendLaterメソッドを使用して同じことができます。

sendLaterメソッドは、sendメソッドと同じパラメーターを受け入れます。ただし、メールを即座に送信する代わりに、Mailメッセンジャーを使用してキューに入れます。

await mail.send((message) => {
await mail.sendLater((message) => {
message
.to(user.email)
.from('info@example.org')
.subject('メールアドレスの確認')
.htmlView('emails/verify_email', { user })
})

デフォルトでは、mailメッセンジャーはインメモリキューを使用します。つまり、プロセスが保留中のジョブで終了すると、キューはジョブを破棄します。アプリケーションのUIが手動のアクションでメールの再送信を許可している場合、これは大きな問題ではありません。ただし、カスタムメッセンジャーを設定し、データベースをバックエンドキューとして使用することもできます。

メールのキューイングにBullMQを使用する

npm i bullmq

次の例では、mail.setMessengerメソッドを使用して、ジョブを保存するためにbullmqを使用するカスタムキューを設定します。

ジョブにはコンパイルされたメール、ランタイム設定、およびメーラー名が含まれます。後でこれらのデータを使用してワーカープロセス内でメールを送信します。

import { Queue } from 'bullmq'
import mail from '@adonisjs/mail/services/main'
const emailsQueue = new Queue('emails')
mail.setMessenger((mailer) => {
return {
async queue(mailMessage, config) {
await emailsQueue.add('send_email', {
mailMessage,
config,
mailerName: mailer.name,
})
}
}
})

最後に、キューワーカーのコードを記述します。アプリケーションのワークフローに応じて、ジョブを処理するために別のプロセスを起動する必要があるかもしれません。

次の例では:

  • emailsキューからsend_emailという名前のジョブを処理します。
  • ジョブデータからコンパイルされたメール、ランタイム設定、およびメーラー名にアクセスします。
  • mailer.sendCompiledメソッドを使用してメールを送信します。
import { Worker } from 'bullmq'
import mail from '@adonisjs/mail/services/main'
new Worker('emails', async (job) => {
if (job.name === 'send_email') {
const {
mailMessage,
config,
mailerName
} = job.data
await mail
.use(mailerName)
.sendCompiled(mailMessage, config)
}
})

以上です!mail.sendLaterメソッドを引き続き使用できます。ただし、今回はメールがredisデータベースにキューイングされます。

メーラーの切り替え

mail.useメソッドを使用して、設定ファイルで定義されたメーラー間を切り替えることができます。mail.useメソッドは、メーラーの名前(設定ファイルで定義された名前)を受け入れ、Mailerクラスのインスタンスを返します。

import mail from '@adonisjs/mail/services/main'
mail.use() // Instance of default mailer
mail.use('mailgun') // Mailgun mailer instance

以下の例では、メーラーインスタンスを使用してメールを送信するために mailer.send または mailer.sendLater メソッドを呼び出すことができます。

await mail
.use('mailgun')
.send((message) => {
})
await mail
.use('mailgun')
.sendLater((message) => {
})

メーラーインスタンスはプロセスのライフサイクルの間キャッシュされます。既存のインスタンスを破棄し、新しいインスタンスをゼロから作成するために mail.close メソッドを使用できます。

import mail from '@adonisjs/mail/services/main'
/**
* トランスポートを閉じてインスタンスをキャッシュから削除します
*/
await mail.close('mailgun')
/**
* 新しいインスタンスを作成します
*/
mail.use('mailgun')

テンプレートエンジンの設定

デフォルトでは、メールパッケージはメールの HTML および プレーンテキスト コンテンツを定義するために Edge テンプレートエンジン を使用するように設定されています。

ただし、次の例に示すように、Message.templateEngine プロパティをオーバーライドしてカスタムテンプレートエンジンを登録することもできます。

参照: メールコンテンツの定義

import { Message } from '@adonisjs/mail'
Message.templateEngine = {
async render(templatePath, data) {
return someTemplateEngine.render(templatePath, data)
}
}

イベント

@adonisjs/mail パッケージによってディスパッチされるイベントのリストを表示するには、イベントリファレンスガイドを参照してください。

メッセージの設定

メールのプロパティは、mail.send または mail.sendLater メソッドを使用して作成されたコールバック関数に提供される Message クラスを使用して定義されます。

import { Message } from '@adonisjs/mail'
import mail from '@adonisjs/mail/services/main'
await mail.send((message) => {
console.log(message instanceof Message) // true
})
await mail.sendLater((message) => {
console.log(message instanceof Message) // true
})

件名と送信元の定義

message.subject メソッドを使用してメールの件名を定義し、message.from メソッドを使用してメールの送信元を定義できます。

await mail.send((message) => {
message
.subject('メールアドレスの確認')
.from('info@example.org')
})

from メソッドは、メールアドレスを文字列または送信者名とメールアドレスのオブジェクトとして受け入れます。

message
.from({
address: 'info@example.com',
name: 'AdonisJS'
})

送信者は、個々のメッセージに明示的な送信者が定義されていない場合に、グローバルに定義された設定ファイル内の送信者が使用されます。

const mailConfig = defineConfig({
from: {
address: 'info@example.com',
name: 'AdonisJS'
}
})

受信者の定義

message.tomessage.cc、および message.bcc メソッドを使用してメールの受信者を定義できます。これらのメソッドは、メールアドレスを文字列または受信者名とメールアドレスのオブジェクトとして受け入れます。

await mail.send((message) => {
message
.to(user.email)
.cc(user.team.email)
.bcc(user.team.admin.email)
})
await mail.send((message) => {
message
.to({
address: user.email,
name: user.fullName,
})
.cc({
address: user.team.email,
name: user.team.name,
})
.bcc({
address: user.team.admin.email,
name: user.team.admin.fullName,
})
})

複数の cc および bcc 受信者を、メールアドレスの配列またはメールアドレスと受信者名のオブジェクトの配列として定義できます。

await mail.send((message) => {
message
.cc(['first@example.com', 'second@example.com'])
.bcc([
{
name: 'First recipient',
address: 'first@example.com'
},
{
name: 'Second recipient',
address: 'second@example.com'
}
])
})

message.replyTo メソッドを使用して replyTo メールアドレスを定義することもできます。

await mail.send((message) => {
message
.from('info@example.org')
.replyTo('noreply@example.org')
})

メールコンテンツの定義

message.html または message.text メソッドを使用してメールの HTML および プレーンテキスト コンテンツを定義できます。

await mail.send((message) => {
/**
* HTML コンテンツ
*/
message.html(`
<h1> メールアドレスの確認 </h1>
<p> <a href="https://myapp.com">こちらをクリック</a>してメールアドレスを確認してください</a>
`)
/**
* プレーンテキストコンテンツ
*/
message.text(`
メールアドレスの確認
メールアドレスを確認するには、https://myapp.com にアクセスしてください
`)
})

Edge テンプレートの使用

インラインコンテンツを記述することは煩雑な場合があるため、代わりにEdgeテンプレートを使用することもできます。すでに Edge の設定 を行っている場合は、message.htmlView および message.textView メソッドを使用してテンプレートをレンダリングできます。

テンプレートの作成
node ace make:view emails/verify_email_html
node ace make:view emails/verify_email_text
コンテンツの定義に使用する
await mail.send((message) => {
message.htmlView('emails/verify_email_html', stateToShare)
message.textView('emails/verify_email_text', stateToShare)
})

メールマークアップのための MJML の使用

MJMLは、すべてのメールクライアントでメールが見栄え良く表示されるようにするための複雑なHTMLを書かずにメールを作成するためのマークアップ言語です。

まず、mjml パッケージをnpmからインストールします。

npm i mjml

インストールが完了したら、Edgeテンプレート内でMJMLマークアップを記述するために@mjmlタグで囲むことができます。

MJMLの出力にはhtmlhead、およびbodyタグが含まれているため、Edgeテンプレート内でこれらを定義する必要はありません。

@mjml()
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>
Hello World!
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
@end

@mjml タグに MJML の設定オプション をプロップスとして渡すこともできます。

@mjml({
keepComments: false,
fonts: {
Lato: 'https://fonts.googleapis.com/css?family=Lato:400,500,700'
}
})

ファイルの添付

message.attach メソッドを使用して、メールに添付ファイルを送信できます。attach メソッドは、添付ファイルの絶対パスまたはファイルシステムのURLを受け入れます。

import app from '@adonisjs/core/services/app'
await mail.send((message) => {
message.attach(app.makePath('uploads/invoice.pdf'))
})

options.filename プロパティを使用して添付ファイルのファイル名を定義することもできます。

message.attach(app.makePath('uploads/invoice.pdf'), {
filename: 'invoice_october_2023.pdf'
})

message.attach メソッドが受け入れるオプションの完全なリストは次のとおりです。

オプション 説明
filename 添付ファイルの表示名。デフォルトは添付ファイルパスのベース名です。
contentType 添付ファイルのコンテンツタイプ。設定されていない場合、contentType はファイルの拡張子から推測されます。
contentDisposition 添付ファイルのコンテンツディスポジションタイプ。デフォルトは attachment です
headers

添付ファイルノードのカスタムヘッダー。ヘッダープロパティはキーと値のペアです

ストリームとバッファからのファイルの添付

message.attachData メソッドを使用して、ストリームとバッファからメール添付ファイルを作成できます。メソッドは、読み込み可能なストリームまたはバッファを第1引数として、オプションオブジェクトを第2引数として受け入れます。

message.attachData メソッドは、mail.sendLater メソッドを使用してメールをキューに入れる場合には使用しないでください。キューに入れられたジョブはシリアライズされ、データベース内に永続化されるため、生データを添付するとストレージサイズが増加します。

また、message.attachData メソッドを使用してストリームを添付すると、メールのキューイングが失敗します。

message.attachData(fs.createReadStream('./invoice.pdf'), {
filename: 'invoice_october_2023.pdf'
})
message.attachData(Buffer.from('aGVsbG8gd29ybGQh'), {
encoding: 'base64',
filename: 'greeting.txt',
})

画像の埋め込み

embedImage ビューヘルパーを使用して、メールのコンテンツ内に画像を埋め込むことができます。embedImage メソッドは、画像を添付ファイルとしてマークし、そのコンテンツIDを画像のソースとして使用します。

<img src="{{
embedImage(app.makePath('assets/hero.jpg'))
}}" />

以下が出力されるHTMLです。

<img src="cid:a-random-content-id" />

次の添付ファイルが自動的にメールペイロードに定義されます。

{
attachments: [{
path: '/root/app/assets/hero.jpg',
filename: 'hero.jpg',
cid: 'a-random-content-id'
}]
}

バッファから画像を埋め込む

embedImage メソッドと同様に、embedImageData メソッドを使用して生データから画像を埋め込むことができます。

<img src="{{
embedImageData(rawBuffer, { filename: 'hero.jpg' })
}}" />

カレンダーイベントの添付

message.icalEvent メソッドを使用して、メールにカレンダーイベントを添付できます。icalEvent メソッドは、最初のパラメータとしてイベントの内容、2番目のパラメータとして options オブジェクトを受け入れます。

const contents = 'BEGIN:VCALENDAR\r\nPRODID:-//ACME/DesktopCalendar//EN\r\nMETHOD:REQUEST\r\n...'
await mail.send((message) => {
message.icalEvent(contents, {
method: 'PUBLISH',
filename: 'invite.ics',
})
})

イベントファイルの内容を手動で定義するのは煩雑な場合があるため、icalEvent メソッドにコールバック関数を渡し、JavaScript APIを使用して招待内容を生成することもできます。

コールバック関数に提供される calendar オブジェクトは、ical-generatornpmパッケージの参照ですので、パッケージのREADMEファイルも参照してください。

message.icalEvent((calendar) => {
calendar
.createEvent({
summary: 'ALS のサポートを追加',
start: DateTime.local().plus({ minutes: 30 }),
end: DateTime.local().plus({ minutes: 60 }),
})
}, {
method: 'PUBLISH',
filename: 'invite.ics',
})

ファイルまたは URL からの招待内容の読み取り

icalEventFromFile メソッドまたは icalEventFromUrl メソッドを使用して、ファイルまたは HTTP URL から招待内容を定義できます。

message.icalEventFromFile(
app.resourcesPath('calendar-invites/invite.ics'),
{
filename: 'invite.ics',
method: 'PUBLISH'
}
)
message.icalEventFromFile(
'https://myapp.com/users/1/invite.ics',
{
filename: 'invite.ics',
method: 'PUBLISH'
}
)

メールヘッダの定義

message.header メソッドを使用して、追加のメールヘッダを定義できます。メソッドは、第1パラメータとしてヘッダキー、第2パラメータとして値を受け入れます。

message.header('x-my-key', 'ヘッダの値')
/**
* 値の配列を定義することもできます
*/
message.header('x-my-key', ['ヘッダの値', '別の値'])

デフォルトでは、メールヘッダはエンコードされ、78バイトを超えないプレーンASCIIメッセージの要件を満たすために折り返されます。ただし、エンコーディングルールをバイパスしたい場合は、message.preparedHeader メソッドを使用してヘッダを設定することもできます。

message.preparedHeader(
'x-unprocessed',
'非ASCII文字を含む非常に長いヘッダまたは値 👮',
)

List ヘッダの定義

メッセージクラスには、List-UnsubscribeList-Help のような複雑なヘッダを簡単に定義するためのヘルパーメソッドが用意されています。Listヘッダのエンコーディングルールについては、nodemailerのウェブサイトを参照してください。

message.listHelp('admin@example.com?subject=help')
// List-Help: <mailto:admin@example.com?subject=help>
message.listUnsubscribe({
url: 'http://example.com',
comment: 'コメント'
})
// List-Unsubscribe: <http://example.com> (コメント)
/**
* ヘッダを複数回繰り返す
*/
message.listSubscribe('admin@example.com?subject=subscribe')
message.listSubscribe({
url: 'http://example.com',
comment: '購読'
})
// List-Subscribe: <mailto:admin@example.com?subject=subscribe>
// List-Subscribe: <http://example.com> (購読)

その他の任意の List ヘッダについては、addListHeader メソッドを使用できます。

message.addListHeader('post', 'http://example.com/post')
// List-Post: <http://example.com/post>

クラスベースのメール

mail.send メソッドのクロージャ内でメールを記述する代わりに、より良い組織化と簡単なテストのために、専用のメールクラスに移動できます。

メールクラスは ./app/mails ディレクトリに保存され、各ファイルが1つのメールを表します。make:mail aceコマンドを実行してメールクラスを作成できます。

参照: メール作成コマンド

node ace make:mail verify_email

メールクラスは BaseMail クラスを拡張し、以下のプロパティとメソッドが用意されています。prepare メソッド内で this.message プロパティを使用してメールメッセージを設定できます。

import User from '#models/user'
import { BaseMail } from '@adonisjs/mail'
export default class VerifyEmailNotification extends BaseMail {
from = 'sender_email@example.org'
subject = 'Verify email'
prepare() {
this.message.to('user_email@example.org')
}
}

from

送信者のメールアドレスを設定します。このプロパティを省略する場合は、送信者を定義するために message.from メソッドを呼び出す必要があります。

subject

メールの件名を設定します。このプロパティを省略する場合は、message.subject メソッドを使用してメールの件名を定義する必要があります。

replyTo

replyTo のメールアドレスを設定します。

prepare

prepare メソッドは、build メソッドによって自動的に呼び出され、メールメッセージの送信の準備をします。

このメソッド内でメールの内容、添付ファイル、受信者などを定義する必要があります。

build 継承

build メソッドは BaseMail クラスから継承されたメソッドです。このメソッドはメールの送信時に自動的に呼び出されます。

このメソッドをオーバーライドする場合は、元の実装を参照してください。

メールクラスを使用してメールを送信する

mail.send メソッドを呼び出し、メールクラスのインスタンスを渡すことでメールを送信できます。例:

メールを送信する
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
await mail.send(new VerifyEmailNotification())
メールをキューに入れる
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
await mail.sendLater(new VerifyEmailNotification())

メールクラスにデータを共有する場合は、コンストラクタ引数を使用してデータを渡すことができます。例:

/**
* ユーザーを作成する
*/
const user = await User.create(payload)
await mail.send(
/**
* メールクラスにユーザーを渡す
*/
new VerifyEmailNotification(user)
)

メールクラスのテスト

メールクラスを使用する主な利点の1つは、テストの経験が向上することです。メールを送信せずにメールクラスをビルドし、メッセージのプロパティに対してアサーションを記述できます。

import { test } from '@japa/runner'
import VerifyEmailNotification from '#mails/verify_email'
test.group('Verify email notification', () => {
test('prepare email for sending', async () => {
const email = new VerifyEmailNotification()
/**
* メールメッセージをビルドし、テンプレートをレンダリングして
* メールのHTMLとプレーンテキストの内容を計算します
*/
await email.buildWithContents()
/**
* メッセージが期待どおりにビルドされたことを確認するためのアサーションを記述します
*/
email.message.assertTo('user@example.org')
email.message.assertFrom('info@example.org')
email.message.assertSubject('Verify email address')
email.message.assertReplyTo('no-reply@example.org')
})
})

次のようにメッセージの内容に対してアサーションを記述することもできます。

const email = new VerifyEmailNotification()
await email.buildWithContents()
email.message.assertHtmlIncludes(
`<a href="/emails/1/verify"> メールアドレスを確認する </a>`
)
email.message.assertTextIncludes('メールアドレスを確認する')

また、添付ファイルに対してもアサーションを記述できます。アサーションはファイルベースの添付ファイルに対してのみ機能し、ストリームや生データには対応していません。

const email = new VerifyEmailNotification()
await email.buildWithContents()
email.message.assertAttachment(
app.makePath('uploads/invoice.pdf')
)

利用可能なアサーションメソッドの一覧については、Message クラスのソースコードを参照してください。

フェイクメーラー

テスト中にメールを送信しないようにするために、フェイクメーラーを使用できます。フェイクメーラーは、メモリ内で送信されたすべてのメールを収集し、それらに対してアサーションを行うための使いやすいAPIを提供します。

次の例では、以下の手順でフェイクメーラーを使用します。

  • mail.fake メソッドを使用して FakeMailer のインスタンスを作成します。
  • /register エンドポイントAPIを呼び出します。
  • フェイクメーラーの mails プロパティを使用して VerifyEmailNotification が送信されたことをアサートします。
import { test } from '@japa/runner'
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
test.group('Users | register', () => {
test('create a new user account', async ({ client, route }) => {
/**
* フェイクモードをオンにする
*/
const { mails } = mail.fake()
/**
* APIコールを行う
*/
await client
.post(route('users.store'))
.send(userData)
/**
* コントローラが VerifyEmailNotification メールを送信したことをアサートする
*/
mails.assertSent(VerifyEmailNotification, ({ message }) => {
return message
.hasTo(userData.email)
.hasSubject('メールアドレスの確認')
})
})
})

テストの記述が終わったら、mail.restore メソッドを使用してフェイクを元に戻す必要があります。

test('create a new user account', async ({ client, route, cleanup }) => {
const { mails } = mail.fake()
/**
* cleanupフックは、テストが正常に終了するかエラーが発生すると実行されます。
*/
cleanup(() => {
mail.restore()
})
})

アサーションの記述

mails.assertSent メソッドは、メールクラスのコンストラクタを最初の引数として受け入れ、期待されるクラスのメールが見つからない場合に例外をスローします。

const { mails } = mail.fake()
/**
* メールが送信されたことをアサートする
*/
mails.assertSent(VerifyEmailNotification)

assertSent メソッドにコールバック関数を渡すことで、メールが期待される受信者に送信されたか、正しい件名を持っているかなどをさらにチェックできます。

コールバック関数はメールクラスのインスタンスを受け取り、.message プロパティを使用して message オブジェクトにアクセスできます。

mails.assertSent(VerifyEmailNotification, (email) => {
return email.message.hasTo(userData.email)
})

コールバック内で message オブジェクトに対してアサーションを実行できます。例:

mails.assertSent(VerifyEmailNotification, (email) => {
email.message.assertTo(userData.email)
email.message.assertFrom('info@example.org')
email.message.assertSubject('メールアドレスの確認')
/**
* すべてのアサーションがパスしたため、メールが送信されたとみなすために true を返します。
*/
return true
})

送信されなかったことをアサートする

mails.assertNotSent メソッドを使用して、メールが送信されなかったことをアサートできます。このメソッドは assertSent メソッドの逆であり、同じ引数を受け入れます。

const { mails } = mail.fake()
mails.assertNotSent(PasswordResetNotification)

送信されたメールの数をアサートする

最後に、assertSentCount メソッドと assertNoneSent メソッドを使用して送信されたメールの数をアサートできます。

const { mails } = mail.fake()
// 2通のメールが合計で送信されたことをアサートする
mails.assertSentCount(2)
// VerifyEmailNotification が1回だけ送信されたことをアサートする
mails.assertSentCount(VerifyEmailNotification, 1)
const { mails } = mail.fake()
// 送信されたメールがないことをアサートする
mails.assertNoneSent()

キューに入れられたメールのアサート

mail.sendLater メソッドを使用してキューに入れられたメールがある場合、次のメソッドを使用してアサーションを記述できます。

const { mails } = mail.fake()
/**
* VerifyEmailNotification メールがキューに入れられたことをアサートする
* オプションで、メールを絞り込むためのファインダ関数を渡すこともできます
*/
mails.assertQueued(VerifyEmailNotification)
/**
* PasswordResetNotification メールがキューに入れられなかったことをアサートする
* オプションで、メールを絞り込むためのファインダ関数を渡すこともできます
*/
mails.assertNotQueued(PasswordResetNotification)
/**
* 合計2通のメールがキューに入れられたことをアサートする
*/
mails.assertQueuedCount(2)
/**
* VerifyEmailNotification メールが1回だけキューに入れられたことをアサートする
*/
mails.assertQueuedCount(VerifyEmailNotification , 1)
/**
* キューに何も入っていないことをアサートする
*/
mails.assertNoneQueued()

送信またはキューに入れられたメールのリストを取得する

テスト中に送信/キューに入れられたメールの配列を取得するには、mails.sentまたはmails.queuedメソッドを使用できます。

const { mails } = mail.fake()
const sentEmails = mails.sent()
const queuedEmails = mails.queued()
const email = sentEmails.find((email) => {
return email instanceof VerifyEmailNotification
})
if (email) {
email.message.assertTo(userData.email)
email.message.assertFrom(userData.email)
email.message.assertHtmlIncludes('<a href="/verify/email"> メールアドレスを確認する</a>')
}

カスタムトランスポートの作成

AdonisJSメールトランスポートは、Nodemailerトランスポートを基にして構築されているため、Mailパッケージに登録する前にNodemailerトランスポートを作成/使用する必要があります。

このガイドでは、nodemailer-postmark-transportをAdonisJSメールトランスポートにラップします。

npm i nodemailer nodemailer-postmark-transport

以下の例では、メールの送信はnodemailerによって行われます。AdonisJSトランスポートは、メッセージをnodemailerに転送し、その応答をMailResponseのインスタンスに正規化します。

import nodemailer from 'nodemailer'
import nodemailerTransport from 'nodemailer-postmark-transport'
import { MailResponse } from '@adonisjs/mail'
import type {
NodeMailerMessage,
MailTransportContract
} from '@adonisjs/mail/types'
/**
* トランスポートが受け入れる設定
*/
export type PostMarkConfig = {
auth: {
apiKey: string
}
}
/**
* トランスポートの実装
*/
export class PostMarkTransport implements MailTransportContract {
#config: PostMarkConfig
constructor(config: PostMarkConfig) {
this.#config = config
}
#createNodemailerTransport(config: PostMarkConfig) {
return nodemailer.createTransport(nodemailerTransport(config))
}
async send(
message: NodeMailerMessage,
config?: PostMarkConfig
): Promise<MailResponse> {
/**
* Nodemailerトランスポートを作成する
*/
const transporter = this.#createNodemailerTransport({
...this.#config,
...config,
})
/**
* メールを送信する
*/
const response = await transporter.sendMail(message)
/**
* 応答を「MailResponse」クラスのインスタンスに正規化する
*/
return new MailResponse(response.messageId, response.envelope, response)
}
}

設定ファクトリ関数の作成

トランスポートをconfig/mail.tsファイル内で参照するためには、トランスポートの実装を返すファクトリ関数を作成する必要があります。

トランスポートの実装と同じファイルに以下のコードを書くことができます。

import type {
NodeMailerMessage,
MailTransportContract,
MailManagerTransportFactory
} from '@adonisjs/mail/types'
export function postMarkTransport(
config: PostMarkConfig
): MailManagerTransportFactory {
return () => {
return new PostMarkTransport(config)
}
}

トランスポートの使用

最後に、postMarkTransportヘルパーを使用して設定ファイル内でトランスポートを参照できます。

import env from '#start/env'
import { defineConfig } from '@adonisjs/mail'
import { postMarkTransport } from 'my-custom-package'
const mailConfig = defineConfig({
mailers: {
postmark: postMarkTransport({
auth: {
apiKey: env.get('POSTMARK_API_KEY'),
},
}),
},
})