今回はパスワードのリセットのテストです。
パスワードのリセットは、以下のステップがあります。
- パスワードリセットのリクエスト画面へ行き、Eメールアドレスを入力してリセットをリクエスト
- メールでパスワードリセットのトークン付きのURLを受信
- パスワードリセット画面へ行きパスワードをリセット
テストケースとして考えられるのは、
- パスワードリセットリクエスト画面へのアクセスができる
- 登録された会員のEメールアドレスでパスワードのリセットのリクエストが成功がする
- 登録されていない会員のEメールアドレスでパスワードのリセットのリクエストが失敗
- 成功したパスワードのリセットのリクエストで受信したURLでパスワードリセットの画面にアクセスができる
- その画面でパスワードリセットができる
パスワードリセットリクエスト画面でのテスト
まずは、画面にアクセスできることを確認。
/** * @test * パスワードリセットをリクエストする画面の閲覧可能 */ public function user_can_view_reset_request() { $response = $this->get('password/reset'); $response->assertStatus(200); }
そして、画面にEメールアドレスを入力して、成功のメッセージが表示されることを確認。
/** * @test * パスワードリセットのリクエスト成功 */ public function valid_user_can_request_reset() { // ユーザーを1つ作成 $user = factory(User::class)->create(); // パスワードリセットをリクエスト $response = $this->from('password/email')->post('password/email', [ 'email' => $user->email, ]); // 同画面にリダイレクト $response->assertStatus(302); $response->assertRedirect('password/email'); // 成功のメッセージ $response->assertSessionHas('status', 'リクエストを受け付けました。パスワードの再設定方法をメールでお知らせします。'); }
ここで注目することとして2点。
まず、$this->from('password/email')->post
で使用されているfrom()
。これがないと、 $response->assertRedirect('password/email');
以下のようなエラーとなります。
There was 1 failure: 1) Tests\Feature\ResetPasswordTest::valid_user_can_request_reset Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'http://localhost/password/email' +'http://localhost'
この画面では成功時には、back()
で前の画面に戻されます。しかし、テストでは前の画面の場所は保存していないので、デフォルトのホーム/になります。password/emailに戻るには、セッションあるいはリファラーで前の画面のURIを記録しておく必要があります。以下のように、TestCase.phpにfrom()
の定義をしておきます。
namespace Tests; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { use CreatesApplication; /** * Set the referer header to simulate a previous request. * * @param string $url * @return $this */ public function from(string $url) { session()->setPreviousUrl(url($url)); return $this; } }
次に注目してもらいたいのは、成功のメッセージが表示されるかどうかのチェック。これは、セッションにstatusのキーが含まれているかどうかをチェックします。成功時には以下のメソッドでstatusにレスの文字列がセッションを介して保存されます。
/** * Get the response for a successful password reset link. * * @param string $response * @return \Illuminate\Http\RedirectResponse */ protected function sendResetLinkResponse($response) { return back()->with('status', trans($response)); }
このリクエスト画面で、今度は失敗するケース。単に、存在しない会員のEメールアドレスを入力してみます。
今度は、セッションに含まれるerrorsに、emailのエントリがあるかをチェックして確認です。
/** * @test * 存在しないメールアドレスでパスワードリセットのリクエストをして失敗 */ public function invalid_user_cannot_request_reset() { // ユーザーを1つ作成 $user = factory(User::class)->create(); // 存在しないユーザーのメールアドレスでパスワードリセットをリクエスト $response = $this->from('password/email')->post('password/email', [ 'email' => 'nobody@example.com' ]); $response->assertStatus(302); $response->assertRedirect('password/email'); // 失敗のエラーメッセージ $response->assertSessionHasErrors('email', '指定のメールアドレスは見つかりませんでした'); }
パスワードリセット画面でのテスト
今度はリクエストではなく、実際にパスワードをリセットする画面に関してのテストです。このテストは2つの部分からなります。
- パスワードリセットをリクエストした後に受信するEメールのURLをもとに、パスワードリセット画面にアクセスできるかを確認
- その画面でパスワードを更新可能なことを確認
少しコードは長くなりますが、まさしくユーザーが辿るステップをそのままここで実現します。
/** * @test * パスワードリセットのトークンでパスワードをリセット */ public function valid_user_can_reset_password() { Notification::fake(); // ユーザーを1つ作成 $user = factory(User::class)->create(); // パスワードリセットをリクエスト $response = $this->post('password/email', [ 'email' => $user->email ]); // トークンを取得 $token = ''; Notification::assertSentTo( $user, ResetPassword::class, function ($notification, $channels) use ($user, &$token) { $token = $notification->token; return true; } ); // パスワードリセットの画面へ $response = $this->get('password/reset/'.$token); $response->assertStatus(200); // パスワードをリセット $new = 'reset1111'; $response = $this->post('password/reset', [ 'email' => $user->email, 'token' => $token, 'password' => $new, 'password_confirmation' => $new ]); // ホームへ遷移 $response->assertStatus(302); $response->assertRedirect('/home'); // リセット成功のメッセージ $response->assertSessionHas('status', 'パスワードはリセットされました!'); // 認証されていることを確認 $this->assertTrue(Auth::check()); // 変更されたパスワードが保存されていることを確認 $this->assertTrue(Hash::check($new, $user->fresh()->password)); }
まず、最初パートでは、リクエストで送信されたEメールに含まれるトークン付きのURLを取得する必要あります。それがなければ、リセット画面にアクセスできません。送信したメールをログにしてそれを読み込んで、URLで抽出ということも可能です。しかし、ここではNotification::fake
を利用することによって、綺麗に簡単に、トークンを抽出します。
そして、そのトークンを利用してリセット画面でEメールアドレスを更新します。更新成功後では、
- リセット成功のメッセージが画面に表示されること
- ユーザが認証されていること
- 更新されたパスワードが保存されていること
を確認します。更新されたパスワードを取得するのに、$user->password
でなく、$user->fresh()->password
となっていることに注意してください。fresh()
により、$userのオブジェクトの中身が更新されます。
他に考えられるテストは?
以上のテストで十分と思われるかもしれませんが、テストケースは切りがないほど考えられます。例えば、
- 期限切れのトークンでリセット画面にアクセスしたとき
- 有効のトークンでアクセスして、トークンを発行したものと違うとEメールアドレスを入力したとき
- リセットの画面でパスワードと確認パスワードが異なるとき
などなど。
最後に今回のテストのコードは以下から利用可能です。
https://github.com/lotsofbytes/larajapan/blob/5.4-test/tests/Feature/ResetPasswordTest.php
メルマガ購読の申し込みはこちらから。