LaravelでEmailとPassword変更についてのテストを書いてみた

2021.01.18

見出し画像

テスト勉強中です。今回は以前の記事のテストに当たるものを書いていきます!

バージョン

PHP 7.4.8
Laravel 7.x

実装する事

emailとpasswordの変更のテスト、異常系も少し書いていきます。

前提

この記事を参考に進めていきますので、ぜひチェックしてみてください。email,password共に編集可能である事です。

大まかな流れ

テストファイル作成

メール

メール編集出来る

メールが空であれば変更出来ない

メールが既に登録されているものであれば変更出来ない

他のユーザーのメール編集画面に遷移出来無い

パスワード

パスワードが編集出来る

確認パスワードが現在のものと一致しなければ編集出来無い

新しいパスワードが8文字以上でなければ編集出来無い

上記の様な流れで進めていきます。

テストファイル作成

下記の様な感じで、コマンドで作成していきます。

php artisan make:test UserManagement

テスト実行コマンドは以下です

./vendor/bin/phpunit --testdox tests/Feature/ファイル名.php

setUp()を設定します。

use RefreshDatabase;

public function setUp(): void         1
{
   parent::setUp();

   $this->user = factory(User::class)->create();
   $this->another_user = factory(User::class)->create();
}

1,  parent::setUp()を記載する事で、各テストが実行される前にsetUp()の中に書いたコードが毎回呼び出される形になります。

factoryの中身

$factory->define(User::class, function (Faker $faker) {
   return [
       'name' => $faker->name,
       'email' => $faker->unique()->safeEmail,
       'email_verified_at' => now(),
       'password' => bcrypt('Test1234'), // password
       'remember_token' => Str::random(10),
   ];
});

メール編集出来る

このテストではメール変更->確認メール送信->トークン確認->変更完了の流れをテストしていく形になります。メール変更処理についてはこちらの記事で紹介しているので是非確認してみてください。

public function test_メール編集出来る()
{
1  $response = $this->actingAs($this->user);
2  $response = $this->get(route('emailchange_before', ['user' => $this->user->id]));
   $response->assertStatus(200);

3  $update_path = route('emailchange_after', ['user' => $this->user->id]);

   // 送信する情報設定
4  $update_data = [
       'new_email' => 'test@test.com'
   ];

5  $response = $this->post($update_path, $update_data);

6  $response->assertSessionHasNoErrors(); // エラーメッセージがないこと

   $response->assertStatus(302); // リダイレクト

   // 正しく変更された場合はメッセージが含まれているか確認
7  $response->assertSessionHas('say','確認メールが送信されました');

   // マイページにリダイレクトされているか
8  $response->assertRedirect(route('my_page', ['user' => $this->user->id])); 

   // email変更テーブルからレコードを取得して来ます。
9  $new_email = EmailReset::get()->first();

10 $response = $this->get(route('emailchange_reset', ['token' => $new_email->token]));

11 $response->assertStatus(302);

12 $response->assertRedirect(route('my_page', ['user' => $this->user->id]));

13 $response->assertSessionHas('say','メールアドレスが正常に変更されました');

   // ユーザー情報を変更したのでrefresh()を使って値を更新する
14 $this->user->refresh();

   // ユーザーのemailの値が変更されているか確認
15 $this->assertEquals($update_data['new_email'], $this->user->email);

   // データベースの方も値が変更されているか確認
16 $this->assertDatabaseHas('users', ['email' => 'test@test.com']);

   // email_resetsテーブルの情報はなくなっている事を確認
17 $this->assertDatabaseMissing('email_resets', ['id' => $new_email->id]);

}

1,  ユーザーをactingAs()メソッドでログイン状態にします。

2,  email編集画面まで遷移します。route()メソッドを使用してルーティングの名前で指定しています。

3,  postで編集するアクションに飛ばすパスを定義しています。引数にはユーザーの情報を含めます。引数やルート名,アクションなどはphp artisan route:listで確認できます。

4,   変更するemailの値を設定します。new_emailとしている部分についてはコントローラー側でどのような値で新しいemailを受け取っているか?もしくはview側でname属性は何になっているかを確認して適宜変更してください。

5,  postで3,4で定義した引数を持ってコントローラーに飛ばしています。

6,  処理がうまく進んだ場合はエラーメッセージがないことを確認します。

7,  処理が終わりリダイレクトで戻ってきた際に、メッセージが含まれているかを確認しています。リダイレクトなのでassertSessionHas()メソッドです。

8,  リダイレクトで戻ってきているページはマイページになっているかをassertRedirect()メソッドで確認しています。

9,  EmailResetテーブルから変更待ちのレコードを取得してきます。

10,  9で取得した値のトークンを使用してルートメソッドで確認処理のアクションに飛ばします。これは確認メールが届いてこちらのメールをクリックして認証を完了してくださいのリンクをクリックしたときになります

11,  リダイレクト処理なので302ステータスを期待します。

12,  8と同じ考え方です。マイページにリダイレクトされているかを確認しています。

13,  7と同じ考え方です。メッセージが含まれているか確認していますが、リダイレクトなのでassertSessionHas()メソッドです。(assertSee()で確認したい場合はgetで一旦ページに遷移するような形になるんですかね。)

14,  refresh()を使ってユーザーの情報を更新します。これしないとemailの情報が古いまま残るので後の処理がうまくいかないので大事です。

15,  assertEquals()メソッドを使用して4で定義したemailが現在のユーザーのemailに反映されているかを確認します。

16,  assertDatabaseHas()メソッドでデータベースの情報も変更したものに書き換わっているか確認します。

17,  9で取得したEmailResetテーブルのレコードが消されていることを確認します。消すような処理をしている場合。参考

メールが空であれば変更出来ない

異常系のテストになります。メールが空であればエラーメッセージが表示されるか?という流れで進めていきます。

public function test_メールが空であれば変更出来ない()
{
   $response = $this->actingAs($this->user);
1  $response = $this->get(route('emailchange_before', ['user' => $this->user->id]));
   $response->assertStatus(200);

2  $update_path = route('emailchange_after', ['user' => $this->user->id]);

   // 送信する情報設定
3  $update_data = [
       // emailの値をnullにする
       'new_email' => null
   ];

4  $response = $this->post($update_path, $update_data);

   $response->assertStatus(302); // リダイレクト

   // 編集ページにリダイレクトされているか
5  $response->assertRedirect(route('emailchange_before', ['user' => $this->user->id])); 

   // エラーメッセージが含まれているか確認
6  $response->assertSessionHasErrorsIn('新しいメールアドレスを入力してください');

}

1,  route()メソッドを使ってルート名指定で編集ページに遷移する。

2,  確認メールを送る処理をするアクションへのパスを定義します。

3,  空の場合のテストをしたいのでnullを代入します。

4,  2,3で設定した情報を持ってコントローラーの確認メール送信の処理に飛ばします。

5,  上手くいかなかった場合にリダイレクトでemail編集画面に戻ってきているか確認する。

6,  assertSessionHasErrorsIn()メソッドでエラーメッセージが含まれているか確認しています。リダイレクトなのでassertSee()では、上手くいかないところに注意です。

メールが既に登録されているものであれば変更出来ない

これは変更しようとしたメールアドレスが他のユーザーと一致してしまった時のテストになります。

同じような処理の部分は説明省いてます。

public function test_メールが既に登録されているものであれば変更出来ない()
{
   $response = $this->actingAs($this->user);
   $response = $this->get(route('emailchange_before', ['user' => $this->user->id]));
   $response->assertStatus(200);

   $update_path = route('emailchange_after', ['user' => $this->user->id]);

   // 送信する情報設定
1  $update_data = [
       // emailの値を他のユーザーのものにする
       'new_email' => $this->another_user->email
   ];

2  $response = $this->post($update_path, $update_data);

   $response->assertStatus(302); // リダイレクト

   // マイページにリダイレクトされているか
   $response->assertRedirect(route('emailchange_before', ['user' => $this->user->id])); 

   // エラーメッセージが含まれているか確認
3  $response->assertSessionHasErrorsIn('もうすでに登録されています');

}

1,  変更する情報として、setUp()で作成しているユーザーのemailを指定します。(他のユーザーのemailとの重複を再現)

2,  1の情報を持ったままコントローラー側の処理に飛ばします。

3,   assertSessionHasErrorsIn()メソッドでエラーメッセージが含まれているかを確認する。リダイレクト時なのでassertSee()ではありません。

他のユーザーのメール編集画面に遷移出来無い

他のテストでもあるような形のやつです。他人の編集ページには遷移できないよってやつです。

public function test_他のユーザーのメール編集画面に遷移出来無い()
{
1  $response = $this->actingAs($this->another_user);

2  $response = $this->get(route('emailchange_before', ['user' => $this->user->id]));

3  $response->assertStatus(403);
}

1,  $this->another_userでログインする。setUp()で2人作ってます。

2,  $this->userの編集ページに遷移しようとする。

3,  自分はコントローラーにabort(403)を書いているので403ステータスを期待してますが、適宜302などに変更してください。

パスワードが編集出来る

次はパスワード編です。基本的に流れはemailと似ています。確認メール送信とかはないですが。。

public function test_パスワード編集出来る()
{
   $response = $this->actingAs($this->user);
   $response = $this->get(route('passwordchange_before', ['user' => $this->user->id]));
   $response->assertStatus(200);

1  $update_path = route('passwordchange_after', ['user' => $this->user->id]);

   // 変更するパスワードの値を設定
2  $password = bcrypt('password1234');

   // 送信する情報設定
3  $update_data = [
       // factoryで作成した時のパスワードの値
       'currentpassword' => 'Test1234',
       'password' => $password,
       'password_confirmation' => $password,
   ];

4  $response = $this->post($update_path, $update_data);

   $response->assertSessionHasNoErrors(); // エラーメッセージがないこと

   $response->assertStatus(302); // リダイレクト

   // 正しく変更された場合はメッセージが含まれているか確認
5  $response->assertSessionHas('say','パスワードが正しく変更されました');

   // マイページにリダイレクトされているか
6  $response->assertRedirect(route('my_page', ['user' => $this->user->id])); 

   // ユーザー情報を変更したのでrefresh()を使って値を更新する
7  $this->user->refresh();

   // 変更したパスワードがユーザーのパスワードと一致しているか確認
8  $this->assertTrue(Hash::check($password, $this->user->password));
}

1,  パスワードを変更するページに遷移します。route()メソッドを使ってuser->idも引数としてルート名で指定しています。

2,  bcrypt()を使用して変更するパスワードを定義します。

3,  コントローラーに送信する情報を定義します。中身は現在のパスワード(確認用)と2で定義した変更するパスワードです。

4,  1,3で定義したパスと情報を持ってコントローラーに飛ばしています。

5,  コメントアウトに書いてある通り、メッセージが含まれているかの確認をしています。assertSessionHasErrorsIn()メソッドですね。

6,  リダイレクトで戻ってきているページがマイページになっているのかを確認。

7,  emailの時と同様で一度ユーザーの情報をリセットして、内容を更新します。

8,  assertTrue()とHash::check()を使用してユーザーのパスワードの値が変更した値と一致する確認しています。

パスワードが現在のものと一致しなければ編集出来無い

変更する際にパスワード確認をしますが、その際に現在のパスワードと一致しなければ編集出来ないと言うことを確認します。

public function test_パスワードが現在のものと一致しなければ編集出来無い()
{
   $response = $this->actingAs($this->user);
   $response = $this->get(route('passwordchange_before', ['user' => $this->user->id]));
   $response->assertStatus(200);

   $update_path = route('passwordchange_after', ['user' => $this->user->id]);
1  $password = bcrypt('password1234');
2  $update_data = [
       // factoryで作成した時とは違うパスワードの値
3      'currentpassword' => 'Test0000',
       'password' => $password,
       'password_confirmation' => $password,
   ];

4  $response = $this->post($update_path, $update_data);

   $response->assertStatus(302); // リダイレクト

5  $response->assertSessionHas('say','現在のパスワードが間違っています。');

6  $response->assertRedirect(route('passwordchange_before', ['user' => $this->user->id])); 
}

1,   変更するパスワードの値を設定する。

2,  コントローラーに送る情報を設定します。現在のパスワードと新しいパスワードになります。キーの名前はviewのname属性と一致させます

3,  currentpasswordについては一致しないものを設定します。(その場合のテストなんで)

4,  1,3の情報を元にコントローラーに処理を飛ばします。

5,  リダイレクトで跳ね返された時にセッションにエラーメッセージが含まれているか確認している。

6,  リダイレクトで戻ってきているページがマイページになっていることを確認する。

新しいパスワードが8文字以上でなければ編集出来無い

新しく変更されるパスワードが8文字以下の場合(不正な値)は編集出来ないことを確認するテストです。

public function test_新しいパスワードが8文字以上でなければ編集出来無い()
{
   $response = $this->actingAs($this->user);
   $response = $this->get(route('passwordchange_before', ['user' => $this->user->id]));
   $response->assertStatus(200);

   $update_path = route('passwordchange_after', ['user' => $this->user->id]);

   $update_data = [
       'currentpassword' => 'Test1234',
1      'password' => 'Nnnnnnm',
       'password_confirmation' => 'Nnnnnnm',
   ];

2  $response = $this->post($update_path, $update_data);

   // 求めるエラーメッセージ
3  $response->assertSessionHasErrorsIn("新しいパスワードは8文字以上で入力してください");

   $response->assertStatus(302); // リダイレクト

4  $response->assertRedirect(route('passwordchange_before', ['user' => $this->user->id])); 
}

1,  コントローラーに送る値として設定する新しいパスワードの中身を7文字に設定します。

2,  設定した情報を持ってコントローラー側の処理に飛ばします。

3,  assertSessionHasErrorsIn()メソッドで指定したエラーメッセージが含まれているか確認しています。リダイレクト時にメッセージ確認したい場合に使えるやつですね!

4,  リダイレクトで戻っているページが変更ページであることを確認しています。

まとめ

異常系なんかはもっとテストする部分があると思いますので、参考にしながら適宜増やしてみてください。

コントローラーに送る情報として設定する部分はname属性をキー名にするところに注意です。検証ツールとかで確認出来ますのでぜひ!

リダイレクト時に表示されるメッセージの確認を行う場合に便利なのがassertSessionHasErrorsIn()メソッドです。最初はassertSee()でやってて少しハマりましたw

今回もこんな感じで!

以上!

参考記事

https://qrunch.net/@memomomomomo/entries/SAYdb7VfIa9Cd0mx