こんにちは。皆さんE2Eテスト書いてますか?
今回は自分がリグレッションテストの自動化に取り組む中で、特に自動化による恩恵が大きかった「メール周りのテスト自動化」について紹介したいと思います。
やりたかったこと
ユーザー新規登録のE2Eテストを自動化したかったのですが、登録時にメールで送信される認証URLを受け取る必要がありました。
「登録時にユーザーにメールが送信される」という部分も自動テストで担保したかったので、ちゃんとメール送信を確認できるようにしたかった感じです。
どうしたか
弊社ではメール送信にMandrill を利用しています。
MandrillにはTestModeという機能があり、要はSandboxなのですが、「テスト用のAPIキーを使って送信したメールは実際に送信されることはない」という機能です。名前の通りテストに最適です。
こんな感じのイメージです。
やってみよう
Mandrillの登録とかそういうのは本筋から逸れるんで省略します。ごめんなさい。
MandrillのTestModeをONにする
右上のアカウント名のところをクリックすると Turn on Test Mode
というボタンが出てくるのでクリックします。
全体的に黄色い感じになりましたね。
テスト用APIキーを取得する
Settings > New API Key
からAPIキーを作成します。
このとき、必ず Test Key
にチェックを入れて下さい。
アプリからメールを送信する
上記で取得したAPI Keyを使ってメールを送信してください。
お使いのフレームワークなりでMandrill対応していればそのフレームワークのドキュメントを読んでもらうのが良いでしょうし、SMTPのパスワードにAPI Keyを入れてもOKです。
送信すると、Outbound
タブからメールが見れるようになります。反映まで20〜30秒くらい時間がかかるようです。
送信したメールをAPIで確認する
Mandrill APIにはSDKが用意されているので、ありがたく使わせて頂きましょう。以下はNodeJSのサンプルです。検索結果から直近1件の明細を取得します。
import { Mandrill } from 'mandrill-api';
import * as sleep from 'sleep-promise'
async function fetchOne(query, api_key) {
const client = new Mandrill(api_key)
const search = (query, api_key) => {
return new Promise((resolve, reject) => {
client.messages.search(
{
query,
api_key,
limit: 100
},
result => {
resolve(result)
},
err => {
reject(err)
}
)
})
}
let searchResult = await search(query, api_key)
for (var i = 0; i < 10; i++) {
if (searchResult['length'] === 0) {
await sleep(10000)
searchResult = await search(query, api_key)
}
}
return new Promise((resolve, reject) => {
client.messages.content(
{
id: searchResult[0]._id
},
result => {
resolve(result)
},
err => {
reject(err)
},
)
})
}
前項でも書きましたが、メールを送ってからMandrillで確認できるようになるまで20〜30秒ほど時間がかかるので、何度かリトライするようにしています。
大まかな処理の流れとしては
-
search
でメールを検索する - 1件以上結果が返ってくるまでリトライする
- 1件以上返ってきたら、メッセージIDをもとに
messages.content
を叩く
messages.content
は非同期で返ってくるので、Promiseを返すようにしています。
fetchOne()
の引数 query
に指定するクエリは https://mandrill.zendesk.com/hc/en-us/articles/205583137-How-to-Search-Outbound-Activity-in-Mandrill を参照してください。例えば、「hoge@example.com 宛かつ件名が ユーザー登録完了のお願い
」であれば下記のようになります。
query = 'full_email:hoge@example.com AND subject:ユーザー登録完了のお願い'
戻り値をどうにかする
上記のサンプルを使って実際にメールを受け取ってみましょう。
console.log(await mail.fetchOne("full_email:hoge@example.com AND subject:ユーザー登録完了のお願い'"))
すると、こんな感じのJSONが出力されます。
{
"headers": {
"Message-Id": "<2527db4835f0d47904dd1a28fb8afc85@swift.generated>",
"Date": "Tue, 11 Dec 2018 15:51:43 +0900",
"From": "webmaster@example.com",
"To": "hoge@example.com",
"Mime-Version": "1.0",
"Content-Type": "text/html; charset=utf-8",
"Content-Transfer-Encoding": "quoted-printable"
},
"html": "(省略)",
"from_email": "webmaster@example.com",
"from_name": "webmaster",
"to": { "email": "hoge@example.com", "name": "" },
"subject": "ユーザー登録完了のお願い",
"attachments": [],
"text": "(省略)",
"ts": 1544511103,
"tags": [],
"_id": "61aa5497443d485a9747f1af8ab3d0d1"
}
メール本文は html
や text
に入りますので、ここをパースすれば今回の目的である認証URLが取得できます。
注意点として、 html
に入るURLはMandrill経由でリダイレクトされるURLになっていますので、特別な理由がなければ text
から取るのが良いでしょう。
おわりに
メール周りは手動テストするのもめんどくさいので、自動化で楽しやすい部分ですね!みなさんもどんどんやってみてください!