今回はパスワードのリセットのテストです。
パスワードのリセットは、以下のステップがあります。
- パスワードリセットのリクエスト画面へ行き、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