今回はパスワードリセットのテストです。リセットリンクをクリックしてメールアドレスを入力、MailHogに届いたメールの確認、パスワードリセット実行、という一連のユーザーの操作をCypress13.xで実装します。
Cypressのセットアップや画面表示テストなど、以前の記事は以下からご覧いただけます。
Cypressセットアップ・実行方法
Cypress画面表示のテスト
Cypressログイン・ログアウトのテスト
Cypressでパスワード再設定のテスト
引き続きLaravel Breezeのログイン画面を使ってテストします。テストの流れは、少し項目が多いですが以下のような形で進めようと思います。
- ログイン画面で「パスワードを忘れた?」というリンクをクリック
- パスワードリセットのリクエスト画面でメールアドレスを入力・送信
- 受信したメールのリンクからパスワードリセット画面へ遷移
- パスワードリセットを実行
- 旧パスワードでログインが失敗することを確認
- 新しいパスワードでログインが成功することを確認
受信メールの確認にはMailHogを使用しますので、もしインストールがまだの場合は環境に合わせてインストールくださいね。
Laravelの.envは以下のように設定しています。
... MAIL_MAILER=smtp MAIL_HOST=localhost MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="local@example.com" MAIL_FROM_NAME=メールテスト ...
事前準備1: cypress-mailhog
CypressからMailHogのデータを取得するための便利なコマンドが提供されているパッケージ、cypress-mailhogを使用します。
以下のコマンドでインストールできます。執筆時点で最新の2.4.0がインストールされました。
$ npm install --save-dev cypress-mailhog
また、以下の設定が必要です。まずはcypress/support/commands.jsファイルにインポート。
import 'cypress-mailhog'; ...
そして、cypress.config.jsファイルのenvにMailHogのURLを追記します。私の環境ではhttp://localhost:8025にてMailHogのUIが確認できます。
import { defineConfig } from "cypress";
export default defineConfig({
env: {
mailHogUrl: "http://localhost:8025", // MailHogのURLをここに追記
},
e2e: {
baseUrl: 'http://127.0.0.1:8000',
},
});
これでCypressのテスト内でcypress-mailhogのコマンドが使用できるようになりました。
具体的には以下のように、cy.に続けてCypressコマンドのように使用できます。mhGetAllMails()はメールボックスのメールを最大50件取得するコマンドで、mhFirst()は、直前のコマンドで取得したメールから最初の1件を抽出するコマンドです。
cy.mhGetAllMails() //メールを最大50件取得 cy.mhGetAllMails().mhFirst() //メールの最初の1件を取得
事前準備2: mailparser
次に、mailparserをインストールします。今回は届いたメールの本文を確認する必要があるのですが、先ほどのcypress-mailhogで取得したメールの本文を見てみると、以下のようにテキストとして読めない状態です。
そのためmailparserを使用して、人間が読めるように解析する必要があります。
以下のコマンドでインストール。
$ npm install mailparser --save-dev
こちらも2ファイルに設定が必要です。まずcypress/support/commands.jsにカスタムコマンドを追加します。先ほどインストールしたcypress-mailhogのimport文も含めると、以下のようになります。
import 'cypress-mailhog'; // cypress-mailhog 用の記述
...
Cypress.Commands.add('mhParseMail', { prevSubject: true }, (mail) => {
return cy.task('parse-mail', { mail: mail.Raw.Data })
})
...
カスタムコマンド名は任意ですが、mhParseMailとしました。
そして、cypress.config.jsにmailparserのimportとtaskを追加します。taskとは、テストコード内からブラウザ外で必要な作業を呼び出すための機能です。taskのドキュメントはこちら
先ほどcypress-mailhogで同ファイルに書き込んだものと合わせると、最終的には以下のようになります。
import { defineConfig } from "cypress";
import { simpleParser as parser } from 'mailparser'; // ここでmailparserをインポート
export default defineConfig({
env: {
mailHogUrl: "http://localhost:8025"
},
e2e: {
baseUrl: 'http://127.0.0.1:8000',
setupNodeEvents(on, config) { // タスク用の追記ここから
on('task', {
'parse-mail': ({ mail }) => parser(mail), // タスクを定義
})
}, // タスク用の追記ここまで
},
});
on('task', {...})がタスクを定義している箇所です。task名は任意ですが、ここではparse-mailとしました。カスタムコマンドmhParseMail()を実行するとparse-mailというtaskが呼ばれ、mailparserによりメール解析が実行される、という流れです。
ここまで設定できたらテストコードからの呼び出しは簡単で、cypress-mailhogで提供されているmhGetAllMails().mhFirst()に繋いで以下のように使用します。
cy.mhGetAllMails().mhFirst().mhParseMail().then((mail) => {
mail.subject // 件名を取得
mail.from.value[0].address // Fromを取得
mail.text // 本文を取得
}
取得したメールの1通目のデータをmhParseMail()で解析してからthen()に渡します。受け取ったmailオブジェクトはmailparserの形式になっているため、上の例のようにmail.subjectで件名を、mail.from.value[0].addressでメールのfromを、mail.textで本文を取得できます。
他にもプロパティが色々ありますのでmailparserのドキュメントをご確認ください。
wrap
ここまでで事前準備は完了ですが、最後に今回新しく使用するコマンドwrap()をご紹介します。
cypress-mailhogやmailparserから受け取ったメールデータはJavaScriptのオブジェクトです。このデータをCypressのアサーションコマンドで扱うために、以下のようにwrap()コマンドでラップします。
cy.mhGetAllMails().mhFirst().mhParseMail().then((mail) => {
cy.wrap(mail.subject).should('eq', 'パスワードのリセット方法について');
}
これで、Cypressコマンドを繋いでメールの件名が期待した内容がどうかを検証できるようになります。
Cypressでパスワードリセットのテスト
以下がパスワードリセットのテストコード全文になります。
it('パスワードリセットのテスト', () => {
cy.visit('/login');
cy.contains('a', 'パスワードを忘れた').click();
// パスワードリセットページに遷移したことを確認
cy.url().should('include', '/forgot-password');
// メールアドレス入力
cy.get('input[name="email"]').type(Cypress.env('email'));
// パスワードリセットメール送信ボタンをクリック
cy.contains('button', 'パスワードリセットメールを送信').click();
// 取得した一覧のうち、1件目(最新)のメール本文を取得
cy.mhGetAllMails().mhFirst().mhParseMail().then((mail) => {
cy.wrap(mail.subject).should('eq', 'パスワードのリセット方法について');
cy.wrap(mail.from.value[0].address).should('eq', 'from@example.com');
// メールの本文からパスワードリセットのためのリンクを抽出
const hrefRegex = /https?:\/\/[^\s/"']+\/reset-password\/[^\s/"']+/;
let match = hrefRegex.exec(mail.text)
// パスワードリセットの画面へ遷移
cy.visit(match[0])
cy.get('input[name="email"]').type(Cypress.env('email')); // メールアドレス入力
cy.get('input[name="password"]').type('new-password'); // 新しいパスワード
cy.get('input[name="password_confirmation"]').type('new-password'); // パスワードの再入力
// パスワードリセットメール送信ボタンをクリック
cy.contains('button', 'パスワードのリセット').click();
// ログイン画面へ遷移したことを確認
cy.url().should('eq', Cypress.config().baseUrl + '/login');
// 旧パスワードでログインすると失敗する
cy.get('input[name="email"]').type(Cypress.env('email'));
cy.get('input[name="password"]').type(Cypress.env('password'));
cy.get('button[type="submit"]').click();
cy.get('input[name="email"]')
.next('ul')
.should('contain', 'ログイン情報が存在しません。');
// 新しいパスワードで再度ログイン試行(メールアドレスの入力値は残っているので不要)
cy.get('input[name="password"]').type('new-password'); // 新しいパスワード
cy.get('button[type="submit"]').click();
// ダッシュボードに遷移したことを確認
cy.url().should('eq', Cypress.config().baseUrl + '/dashboard');
})
})
ではテストを実行してみます。npx cypress openでCypressを立ち上げて、対象ファイルをクリックします。
成功しました!ですが、このテストではDBのパスワードを変更してしまうため、どこかでパスワードをデフォルトに戻すなどの処理を行う必要があります。
CypressでDBをリセットするには、テスト対象のプロジェクトにDBリセット用のAPIを用意する、DBリセット用のシェルスクリプトを作成してテストから呼び出す・・・などの方法が考えられますが、今回はLaravelのプロジェクトがテスト対象ということもあり、ユニットテストのようにrefreshDatabase()が実行できれば便利ですよね。
ということで、次回はLaravelプロジェクトをCypressでテストするときに便利なパッケージをご紹介します。
参照
Cypressセットアップ・実行方法
Cypress画面表示のテスト
Cypressログイン・ログアウトのテスト



