今回はパスワードリセットのテストです。リセットリンクをクリックしてメールアドレスを入力、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ログイン・ログアウトのテスト