Fippiyのプログラム学習内容アウトプットBlog

日々の学習内容をアウトプットして振り返りを実施する。

Laravel開発、Userテーブルのテストをする【2】パスワードリセットをテストする

ユーザー情報テストを実装しています。

ログインテストが終了したので、次はパスワードリセットをテストします。

前回の記事はこちら。

fippiy.hatenablog.jp

ログインテストの後は新規登録テストとしたかったのですが、メール送付関連のチェックが必要です。

パスワードリセットの手順が参考サイトに一緒にあったので、こちらでメール関連を実装してから新規登録のテストを行う流れとしました。

ということでリセットを先に実施。

今回の目的

パスワードリセットの動作をテストし、コードチェックによる正常性を確認する

なぜやるか

パスワードリセットの動作が問題ないことを担保する。

他のメール送付機能をテストする為、メールアドレス送付のテスト手順を学習する

やりたいこと

パスワードリセットが正常動作していることをテストする

パスワードリセットが失敗する状態を確認する

やったこと

パスワードリセットが正常処理されることをテストする

パスワードリセットが失敗される場合のテストをする 

実施内容

正常処理されることをテストする

テストの実施項目を検討する

ログインテスト同様、まずはエラーなく処理が完了することを確認します。

何をもってOKとするか?

  1. 検証用ユーザーを用意しておく
  2. ログインせずに、パスワードリセットフォームにアクセスできること
  3. リセットを実施するアカウントを指定(メールアドレス入力)
  4. 受付完了のメッセージを表示
  5. メールURLより新パスワード受付フォームへアクセスできること
  6. メールアドレスと新パスワードを入力
  7. トークン照合
  8. 新パスワードへ更新
  9. アプリ内にリダイレクト

これらを検証できれば、パスワードリセットのテストは問題ないでしょうか。

 

テストコードを書く

というわけで、作成してみました。

# ~/tests/Feature/UserTest.php

<?php

〜use(追加分のみ)〜
use Illuminate\Support\Facades\Auth;
use Notification;
use App\Notifications\CustomResetPassword;
use Illuminate\Support\Facades\Hash;

// リセットパスワード成功
public function test_resetPassword_ok()
{
Notification::fake(); // 通知機能使用、実際には通知しない

$response = $this->get('password/reset'); // パスワードリセットページにアクセス
$this->assertFalse(Auth::check()); // Auth認証されていないことを確認
$response->assertStatus(200); // 正常表示されること

$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',
'パスワード再設定用のURLをメールで送りました。'); // 成功のメッセージ

$token = '';
Notification::assertSentTo(
$user,
CustomResetPassword::class,
function ($notification, $channels) use ($user, &$token) {
$token = $notification->token;
return true;
}
); // メール通知を実施(fackにより実際にメールはしない)

$response = $this->get('password/reset/'.$token); // リセットURLページへアクセス
$response->assertStatus(200); // アクセスできること
 
$new = '87654321'; // 新しいパスワード
$response = $this->post('password/reset', [
'email' => $user->email,
'token' => $token,
'password' => $new,
'password_confirmation' => $new
]); // 新パスワードで登録

$response->assertStatus(302); // リダイレクト
$response->assertRedirect('/login'); // ログインページに遷移

$this->assertTrue(Auth::check()); // 認証されていることを確認

$this->assertTrue(Hash::check($new, $user->fresh()->password));
 // 変更されたパスワードが保存されていることを確認
}

一連の動作どおりに記述してみました、意外と長くなりました。

 

コード詳細を確認する

まず、メール送付防止をします。

use Notification;
Notification::fake(); // 通知機能使用、実際には通知しない

実際はメール送付を行うのですが、テストでは送付はしませんので、メール送付されないようにしています。

use設定した上で、fake設定をしています。

 

フォームからメールアドレスを送信する

$response = $this->from('password/email')->post('password/email', [
'email' => $user->email,
]); // リセットをリクエス

パスワードリセットを行うためのメールアドレスを入力するフォームから作成済みユーザーのメールアドレスを入力して送信しています。

 

パスワードリセット用のトークンを発行してメール送付

use App\Notifications\CustomResetPassword;
$token = '';
Notification::assertSentTo(
$user,
CustomResetPassword::class,
function ($notification, $channels) use ($user, &$token) {
$token = $notification->token;
return true;
}
); // メール通知を実施(fackにより実際にメールはしない)

メール送付の記述です。

この内容については、こちらを参考にさせて頂きましたありがとうございます。

readouble.com

Notification::assertSentToによって相手に通知が行われていることを確認しています。

この結果ががtrueとなっていれば通知されていることが担保されます。

 

CustomResetPasswordクラスは、パスワードリセットのメールを作成していた時のクラスですので、ご自身の作成した送付メール用のクラスとして見て下さい。

 

パスワード設定ページで新パスワードを設定する。

use Illuminate\Support\Facades\Hash;
$response = $this->get('password/reset/'.$token); // リセットURLページへアクセス
$response->assertStatus(200); // アクセスできること
 
$new = '87654321'; // 新しいパスワード
$response = $this->post('password/reset', [
'email' => $user->email,
'token' => $token,
'password' => $new,
'password_confirmation' => $new
]); // 新パスワードで登録

新パスワード設定ページへアクセスします。トークンをつなげることで対象ページに遷移します。こちらも、実際に作成したURLに合わせる必要があります。

そして、新ユーザー情報を登録しています。

最後に変更した新パスワードが保存されていれば処理完了です。

 

パスワードリセット失敗時のテストする

テストの実施項目を検討する

次は、失敗時のテストを行います。

何をもってNGとするか

 

リセットアカウント指定フォーム

(パスワードを忘れたメールアドレスを入力)

  • メールアドレス未入力
  • メールアドレスアンマッチ

新パスワード入力フォーム

  • トークンアンマッチ
  • メールアドレス未入力
  • メールアドレスアンマッチ
  • 新パスワード未入力
  • 新パスワード7文字以下
  • 新パスワード確認と不一致

その他確認項目

  • エラーメッセージが出力されていること
  • ユーザーパスワードが変更されていないこと

上記にあげた項目が網羅できれば、失敗時の検証となりそうです。

 

リセットアカウント指定フォームテスト

まず初めにパスワードを忘れた場合はメールアドレスを入力しますので、こちらからテストします。

# ビュー

f:id:Fippiy:20190426161357p:plain

リセットアカウント指定フォーム

このフォームのテストは2つありました。今回、指定したメールアドレスが新規登録されていないテストに関して記載しました。

# ~/tests/Feature/UserTest.php

// リセットパスワード、リクエストフォーム、登録メールなし
public function test_resetPassword_ng_email_notWhere()
{
〜略〜
$user = factory(User::class)->create(); // テストユーザー準備

$response = $this->from('password/email')->post('password/email', [
'email' => 'test@test.com',
]); // リセットをリクエス

$response->assertStatus(302); // リダイレクトされること
$response->assertRedirect('password/email'); // 同画面にリダイレクト

$response->assertSessionHasErrors(['email']); // emailエラーを確認

$this->assertEquals('メールアドレスに一致するユーザーが存在しません。',
session('errors')->first('email')); // エラメッセージを確認
}

factoryによりテストユーザーを生成しておき、リセット用メールアドレスを個別指定で送信。

登録ユーザーに存在しないことから、エラー文をうけとって表示させています。

 

新パスワード入力フォームテスト

新しいパスワードを設定するフォームにたいしてのテストです。

こちらは、新パスワードと新パスワード(確認)が不一致のコードです。

// リセットパスワード、再登録、パスワード(確認)不一致
public function test_resetPassword_ng_confirmationPassword_unMatch()
{
Notification::fake(); // 通知機能使用、実際には通知しない

$response = $this->get('password/reset'); // パスワードリセットページにアクセス
$this->assertFalse(Auth::check()); // Auth認証されていないことを確認
$response->assertStatus(200); // 正常表示されること

$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',
'パスワード再設定用のURLをメールで送りました。'); // 成功のメッセージ

$token = '';
Notification::assertSentTo(
$user,
CustomResetPassword::class,
function ($notification, $channels) use ($user, &$token) {
$token = $notification->token;
return true;
}
); // メール通知を実施(fackにより実際にメールはしない)

$response = $this->get('password/reset/'.$token); // リセットURLページへアクセス
$response->assertStatus(200); // アクセスできること
 
$new = '87654321'; // 新しいパスワード
$newconf = '987654321'; // 確認用パスワード
$response = $this->post('password/reset', [
'email' => $user->email,
'token' => $token,
'password' => $new,
'password_confirmation' => $newconf
]); // 新パスワードで登録(確認パスワード不一致)

$response->assertStatus(302); // リダイレクト
$response->assertRedirect('/password/email'); // 登録ページに遷移

$this->assertFalse(Auth::check()); // 認証されていないこと確認

$response->assertSessionHasErrors(['password']); // emailエラーを確認
$this->assertEquals('パスワードは確認用項目と一致していません。',
session('errors')->first('password')); // エラメッセージを確認

$this->assertFalse(Hash::check($new, $user->fresh()->password));
 // 新しいパスワードが保存されていないことを確認

$this->assertDatabaseHas('users', [
'name' => $user->name,
'email' => $user->email,
'password' => $user->password,
]); // ユーザーが変更されていないことを確認
}

前半は初めのリセットを行うアドレスを入力する処理です。

 

リセットを行うURLへのアクセスにはトークンが一致している必要があります。

アクセス出来る事が確認できることと、実際に変更処理をするときに一致しているかを確認することで担保をとっています。

そして、パスワードが今回は一致しませんので、エラーメッセージを受け取り、一致していないメッセージと照合されることを確認しています。

最後に、照合NGだったので、ユーザーデータが変更されていないことを確認しています。

 

以上で、パスワードリセットに対してのテストは完了です。

※この次に新規登録にたいしてのテストを実装予定していましたが、思っていたより苦戦したので別のテストを実施することにしました。