テストによっては、送信されたメールアドレスの内容をチェックしたり、メール内のリンクをクリックしたりする必要があります。この流れを自動化するもっとも良い方法は、外部のメールサービスとMagicPodのWeb APIコマンドを組み合わせ、送信されたメールの内容を取得することです。
ここでは、MailSlurpを使った方法と、Google Workspace(旧G Suite)の組織に属するGmailのWeb APIを使った方法をそれぞれ紹介します (他にはMailtrapというサービスもありますが、無料版ではメールアドレスを持てないのでここでは紹介しません)。MailSlurpを使った方法は設定が簡単ですが、無料版ではメール送信数等の制限があります。Gmailの方は設定が複雑かつGoogle Workspaceの契約が必要ですが、既にGoogle Workspaceを利用している企業なら送信数制限等なく使えます。使い勝手及び機能性の観点から、MailSlurpの方が導入しやすいと思います。
目次
1. MailSlurpを使った方法
MailSlurpとは
主にテストや開発用途を目的とした、ダミーのメールアドレスと、その送受信が可能なAPIを提供しているWebサービスです。利用言語は英語のみです。
無料版と有料版があり、無料版では以下のような制限があります。
- メールアドレスの生成は月50回まで
- メールの送受信はそれぞれ月100通まで
- 商用利用不可
詳細はこちらの価格表をご確認ください。月約21$のStarterプランにすれば、実用上問題になるケースはあまりないでしょう。
設定方法
トップページ右上の「Sign up」ボタンからアカウント作成を行い、ログイン後DashboardにてAPI Keyをコピーします。
またテスト内で利用するメールアドレスを生成します。
ランダムな値が生成されるため、ドメインを含まない受信トレイのIDをコピーします。
Magic Podのシークレット共有変数にMAIL_API_KEYを定義し、コピーした値をセットします。
また変数に固定値を保存するコマンドを使い、メールアドレスIDを定義し、受信トレイのIDからコピーした値をセットします。
次に、下図のようにWeb APIコールのコマンドを呼び出し、${メールアドレスID}を使い、ユーザー登録などの処理を行ってこのアドレスにメールを送信してください(この処理はWeb APIコールコマンドではなく、テスト対象アプリケーションを操作して行います)。
この時、メールボックスにはテストごとにメールが追加されていくため、テストの冒頭でメールボックスを空にするのがおすすめです。(メールの呼び出しは〜件目で指定をするため)
以下のWeb APIコールで、メールボックスを空にします。
指定する値は次の通りです。
- メソッドに「DELETE」
- URLに「https://api.mailslurp.com/emails」。minCount=1は、受信ボックスのメール合計が1通以上になるまで待機することを意味します。この値は変更可能です。
- ヘッダーに「x-api-key」「${MAIL_API_KEY}」
それでは実際にメールのタイトルや内容を呼び出してみましょう。
指定する値は次の通りです。
- メソッドに「GET」
- URLに「https://api.mailslurp.com/inboxes/${メールアドレスID}/emails?minCount=1」。minCount=1は、受信ボックスのメール合計が1通以上になるまで待機することを意味します。この値は変更可能です。
- ヘッダーに「x-api-key」「${MAIL_API_KEY}」
- 結果に「タイトル」「jsonResponse[0]["subject"]」と「メールID」「jsonResponse[0]["id"]」。0は1件目のメールを意味します。1にすると2件目、2にすると3件目、... となります。 ((最新のものを先頭にするオプションもあるようなのですが、うまく動作しませんでした。))
これで、1件目のメールの「タイトル」を${タイトル}で、「メールID」を${メールID}参照可能になります。
※「メールID」はMailSlurpが受信した各メールに対するID。
続いて、このメールの本文を以下のWeb APIコールで取得します。
指定する値は次の通りです。
- メソッドに「GET」
- URLに「https://api.mailslurp.com/emails/${メールID}/textLines」
- ヘッダーに「x-api-key」「${MAIL_API_KEY}」
- 結果に「本文」「jsonResponse["lines"]」
これで、1件目のメールの「本文」を${本文}で取得可能になります(複数行にわたる本文の場合は1行に連結されます)。
htmlで本文を取り出したい場合にはURLに「https://api.mailslurp.com/emails/${メールID}/html」を指定してください。
あとは、「確認」コマンドでメール本文の内容が期待値通りかチェックしたり、「正規表現に一致した部分を保存」コマンドでメール本文の特定の箇所を取り出したりすることが可能です。
メール本文のURLをクリックしてユーザー認証を完了する、などの処理を行いたい場合は、「正規表現に一致した部分を保存」コマンドでURL部分を取り出した後、
- ブラウザテストの場合は「URL指定で遷移」コマンドでそのURLに遷移する
- モバイルテストの場合は「他のアプリを起動」でモバイルブラウザを立ち上げ (モバイルアプリテストのスクリプトから、モバイルブラウザを開きたい) 、ブラウザバーにそのURLを入力し、遷移するとよいでしょう。
その他
- 送信されたメールの内容は、MailSlurpのDashboardで確認できます。
- MailSlurpにはこの他にも以下のような様々なAPIがあります。詳細はMailSlurpのドキュメントをご確認ください。(現在公式のドキュメントページは改修中です)
- HTMLメール本文からCSSセレクタにマッチした部分を取得(/emails/${メールID}/htmlQuery)
- HTMLメール本文から正規表現にマッチした部分を取得(/emails/${メールID}/contentMatch)
- 特定の条件に合致するメールが受信ボックスに見つかるまで待機し、見つかったらその内容を取得(/waitForMatchingEmails)
- メールアドレスの削除(DELETEメソッドの/inboxes/${メールアドレスID})
- メール送信(POSTメソッドの/inboxes/${メールアドレスID})
- メールのヘッダ情報の取得(/emails/${メールID}の結果からjsonResponse["headers"]["ヘッダ名"]でデータを取得)
2. Google WorkspaceのGmail Web APIを使った方法
「クライアントID」「クライアントシークレット」の取得
まず最初に、Web APIの認証に必要な「クライアントID」「クライアント シークレット」を取得します。
まず、G Suiteに属するテスト用のGoogleアカウント(= Gmailアカウント)を1つ用意します(Web APIでメール内容を取得可能になってしまうため、普段使っているアカウントとは別のものを用意してください)。 ((G Suiteに属していないメールアドレスでもWeb API利用は不可能ではないですが、期限のないトークンを取得する作業は非常に大変です。))
続いて、作成したGoogleアカウントでGoogleのDeveloper Consoleにサインインし、画面左上「プロジェクトの選択」から、好きなプロジェクト名で「プロジェクト」を作成します。
続いて、画面左上メニューの「APIとサービス」>「ダッシュボード」から、「ENABLE APIS AND SERVICES」を選択し、GmailのAPIを選んで有効化します。
APIの認証を行うには、先にOAuthを有効にする必要があります。まず、画面左上メニューの「APIとサービス」>「OAuth同意画面」を選択します。G Suiteのメールアドレスであれば、「User Type」に「内部」を選択できるので、これを選択します。「外部」でもテスト送信は可能ですが、トークンが数日で切れてしまいます。 ((G Suiteのメールアドレスでない場合、「内部」が選択できません。「外部」を選択してGoogleによる確認を受ければ、G Suite以外のメールアドレスでもおそらく期限のないトークンが取得できますが、承認を受けるのは非常に困難だと思います。))。
次の画面では、適当な「アプリケーション名」を指定し、さらに「スコープ」に「メール メッセージと設定の表示」を選択して「保存」を押しましょう。これでOAuthが有効になりました。
続いて「認証情報」を作成します。画面左上メニューの「APIとサービス」>「認証情報」から、「認証情報を作成」>「OAuth クライアントID」を選び、「アプリケーションの種類」を「デスクトップアプリ」にして「作成」を押します。
すると、「クライアントID」と「クライアント シークレット」、JSONファイルのダウンロードボタンが画面に表示されるので、JSONファイルを適当なディレクトリに「credentials.json」という名前で保存します。
「リフレッシュトークン」の取得
続いて、こちらもWeb APIの認証に必要な「リフレッシュトークン」を取得します。
Pythonを使用した手順で説明します。(そのほかのAPIもhttps://developers.google.com/gmail/api/guides/quickstarts-overviewから参照できます)
まず、Pythonがマシンにインストールされていない場合は、こちらの手順に従いインストールします。
次に、Windowsの場合はコマンドプロンプト、Macの場合はターミナルを開き、以下のコマンドを実行します。
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
これでGoogle client libraryがダウンロードできました。
次に、マシン上の適当なディレクトリにエディタを起動して空のファイルを作成します。こちらのページの「コードサンプルをコピー」ボタンを押して内容をコピーし、貼り付けます。
作成したファイルを「quickstart.py」というファイル名で保存します。
「quickstart.py」と同じディレクトリに先ほど保存した「credentials.json」を移動し
コマンドラインから以下を実行します。
cd <quickstart.pyのあるディレクトリ>
python quickstart.py
すると下図のようなページが表示されるので「許可」を押しウィンドウを閉じます。
同じディレクトリに「token.json」というファイルが作成され、そこから「クライアントID」「クライアントシークレット」「リフレッシュトークン」を確認することができるようになります。
メール本文を取得するテストスクリプト
「クライアントID」「クライアントシークレット」「リフレッシュトークン」の3つが揃ったので、これでMagic PodのテストスクリプトからGmailのAPIを呼び出すことができます。
まず、シークレット共有変数にCLIENT_ID、CLIENT_SECRET、REFRESH_TOKENの3つを定義し、それぞれに先ほど取得した値をセットします。
次に、下図のようにWeb APIコールのコマンドを呼び出し、リフレッシュトークンを元に最新の「アクセストークン」を取得します。
指定する値は以下の通りです。
- メソッドに「POST」
- URLに「https://accounts.google.com/o/oauth2/token」
- ヘッダーに「Content-Type」「application/x-www-form-urlencoded」
- ボディの「rawデータ」に「client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&refresh_token=${REFRESH_TOKEN}&grant_type=refresh_token」
- 結果に「ACCESS_TOKEN」「jsonResponse["access_token"]」
これを実行すると、変数ACCESS_TOKENにアクセストークンの値がセットされます ((取得したアクセストークンは、30分以内なら何度も使い回すことができます。)) 。
続いて、このアクセストークンを使い、指定した条件に一致するメールのリストを取得します。今度は、次のようにWeb APIコールを呼び出します。
指定する値は以下の通りです。
- メソッドに「GET」
- URLに「https://www.googleapis.com/gmail/v1/users/me/messages/?q=<検索条件>」。<検索条件>の部分は、Gmailの検索条件の記法がそのまま使えます。例えば、以下のような書き方が可能です (詳しい記法はこちらを参考にしてください)。
- q=new_user12345678 (本文などに「new_user12345678」を含むメールを取得)
- q="from:no-reply@magic-pod.com" (「no-reply@magic-pod.com」から送信されたメールを取得)
- q="subject:New user registration" (タイトルに「New user registration」を含むメールを取得)
- ヘッダーに「Authorization」「Bearer ${ACCESS_TOKEN}」
- 結果に「MAIL_ID」「jsonResponse["messages"][0]["id"]」
これを実行すると、変数「MAIL_ID」に、<検索条件>にマッチする最新のメールのIDが格納されます。
続いて、このMAIL_IDにマッチするメールの本文を取得します。次のようにWeb APIコールを呼び出します。
指定する値は以下の通りです。
- メソッドに「GET」
- URLに「https://www.googleapis.com/gmail/v1/users/me/messages/${MAIL_ID}」
- ヘッダーに「Authorization」「Bearer ${ACCESS_TOKEN}」
- 結果の変数に「MAIL_CONTENTS」と記述し、結果のJavascriptに以下の内容をコピー&ペーストする ((改行文字がなくなりますが、動作上問題はありません))
const inlineMessagePart = function(messagePart) {
let messagePartBodies = [{"mimeType": messagePart.mimeType, "body": messagePart.body}];
if ("parts" in messagePart) {
for (const subMsgPart of messagePart["parts"]) {
messagePartBodies = messagePartBodies.concat(inlineMessagePart(subMsgPart));
}
}
return messagePartBodies;
};
atob([jsonResponse['payload']].map(inlineMessagePart).flat().filter(function(item) { return item.mimeType === "text/plain"; })[0]['body']['data']);
これで、変数MAIL_CONTENTSにメール本文の内容が保存されます。
【補足】変数MAIL_CONTENTSの代入に使用するJavascriptに関して
メールをプログラムで扱う際、そのデータ構造を意識する必要があります。HTMLメールの場合、そのデータは階層構造になることがあります(単純なテキストメールの場合、階層構造にはならない)。そのため、「MAIL_CONTENTS」変数の代入に使用するJavascriptは次のような処理を行なっています。
- メールのデータ構造をフラットにする
- MIMEタイプ が text/plain であるメッセージパートを探す。これは、単純なテキストメールからメール本文を取得すること、あるいは、HTMLメールからHTMLメールが見えない環境向けの代替テキストを取得することを意図しています。
- 2のメッセージパートからデータ (メール本文あるいはHTMLメールの代替テキスト) を取得する。そのデータはBase64文字列なので、UTF8に変換する。
このJavascriptは以下の2つの場合にメール本文の取得に失敗する可能性があります。
1. テスト対象のメールがHTMLメールかつ代替テキストがない場合
ようはMIMEタイプ が text/plain であるメッセージパートがない場合です。この場合は、上記のJavascriptの「item.mimeType === "text/plain"」を「item.mimeType === "text/html"」に書き換ればメール本文が取得できるようになるはずです。
2. MIMEタイプ が text/plain であるメッセージパートが複数箇所ある場合
こちらに該当する場合、意図しないメール本文を取得してしまう可能性があります。この場合、お手数をおかけしますが、MagicPodサポートにご連絡頂けると幸いです。個別の回避策をご案内します。
参考
メールのデータ構造をざっくり理解するにはこちらが参考になります。また、Gmail Web APIでメールが具体的にどのようなデータ構造で表現されているかはこちらが参考になります。