Laravel入門 テストファーストでのToDoのステータス変更機能の作成(第15回)

2021-01-01
4 min read

今回、ToDo 一覧画面にステータス変更機能を追加します。

画面レイアウトと仕様

タスク一覧画面(URL「/」)を下記画面イメージの通り修正します。

taskステータス更新

各タスクの左にステータス変更ボタンを設定します。 各ボタンの仕様は下記表になります。

イベント URL 備考
未着手 /tasks/{taskid}/status/1 get
着手中 /tasks/{taskid}/status/2 get
完了 /tasks/{taskid}/status/3 get
延期 /tasks/{taskid}/status/4 get

ステータスボタンをクリックすると、ステータス変更依頼をサーバ側に送信します。 送信先が、/tasksで、送信方法が get です。

誤ってステータス変更したものを元のステータスに訂正することを考慮して、ステータス変更に制約は設けません。 ステータス変更の制約とは、ステータスを完了としたものは、未着手に変更できないように制限するなどです。

テストケースとテストコード実装

考えられる大まかなテストケースとしては下記になります。

  • ステータスが未着手になること
  • ステータスが着手中になること
  • ステータスが完了になること
  • ステータスが延期になること

上記テストケースをコーディングする前にテストケースクラスを作成します。

php artisan make:test TaskStatusChangeTest

生成後にuse RefreshDatabaseなど必要なものを追記します。

ステータスが未着手になること

新規登録画面のテストのときに使用したdataProviderを使いテストケースを記述します。

public function provideStateTestParams()
{

    return [
         'ステータスが未着手になること' => ['Ready', '2'],
         'ステータスが着手中になること' => ['Doing','3'],
         'ステータスが完了になること' => ['Done', '4'],
         'ステータスが延期になること' => ['notReady', '1'],
    ];
}

dataProviderを使用してテストを行うメソッドのコメントに、 @dataProviderでプロバイダのメソッド名を指定します。

/**
 * @dataProvider provideStateTestParams
 *
 * @param int $beforeStatus
 * @param int $afterStatus
 */
public function testUpdateState($beforeStatus,$afterStatus): void
{
    //1件のタスクを指定のステータスで作成。
    $task = factory(Task::class)->state($beforeStatus)->create();

    //スタータス更新を行います。
    $response = $this->get(route('task.updateStatus',['id' => $task->id,'afterstatus'=>$afterStatus]) );
    //画面が指定先にリダイレクトされていることを確認
    $response->assertRedirect(route('home'));

    //スタータスの更新が行われたかを確認
    $this->assertDatabaseHas('tasks', [
        'id' => $task->id,
        'status' => $afterStatus,
        'created_at' => $task->created_at
    ]);

    //更新日付が新しい日付になったかを確認
    $updatedTask = Task::find($task->id);
    $this->assertGreaterThan(
        $task->updated_at,
        $updatedTask->updated_at,
    );

}

テスト実施

テストを実施します。

$ ./vendor/bin/phpunit tests/Feature/TaskStatusChangeTest.php --testdox

テストはすべてエラーになることを確認します。 テストコードに間違いがある場合は修正します。

実装

router

Web.php に、ルートを追加します。 ボタン別に URL が割り当てられ、全部で URL が 4 種類あります。 しかし、/tasks/{taskid}/status/{afterState}とすれば、ひとつにまとめることができます。 {afterState}には、未着手、着手中、完了、延期のコード値が入ります。

Route::get('/tasks/{id}/status/{afterstatus}', 'TaskController@updateStatus')->where(['id'=>'[0-9]+','afterstatus'=> '[1-4]'])->name('task.updateStatus');

controller

TaskControllerupdateStateを追加します。

  public function updateStatus($id,$afterStatus)
  {
      $task = Task::find($id);
      $task->status = $afterStatus;
      $task->save();
      return redirect(route('home'));
  }

指定された id を key にタスクを取得して、ステータスを指定のものに変更して更新します。 更新後は画面を再表示します。

画面

index.balde.html を修正します。@section('content')@endsectionで囲まれた部分を下記のように書き換えます。

<ul class="list-group">
    @foreach ($tasks as $task)
    <li class="list-group-item d-flex justify-content-between align-items-center">
        <a href="{{ route('task.edit',['id' => $task->id]) }}" dusk="{{ 'edit-' . $task->id }}" >{{ $task->status_name }}-{{ $task->title }}</a>
        <div>
            <a class="btn btn-primary btn-sm mr-1" role="button" href="{{ route('task.updateStatus',['id' => $task->id,'afterstatus'=>1]) }}">
                未着手
            </a>
            <a class="btn btn-primary btn-sm mr-1" role="button" href="{{ route('task.updateStatus',['id' => $task->id,'afterstatus'=>2]) }}">
                着手中
            </a>
            <a class="btn btn-primary btn-sm mr-1" role="button" href="{{ route('task.updateStatus',['id' => $task->id,'afterstatus'=>3]) }}">
                完了
            </a>
            <a class="btn btn-primary btn-sm" role="button" href="{{ route('task.updateStatus',['id' => $task->id,'afterstatus'=>4]) }}">
                延期
            </a>
        </div>
    </li>
    @endforeach
</ul>

それぞれのスタータスボタンと飛び先を追加しました。

テスト

$ ./vendor/bin/phpunit tests/Feature/TaskStatusChangeTest.php --testdox
PHPUnit 9.5.1 by Sebastian Bergmann and contributors.

Task Status Change (Tests\Feature\TaskStatusChange)
 ✔ Update state with ステータスが未着手になること
 ✔ Update state with ステータスが着手中になること
 ✔ Update state with ステータスが完了になること
 ✔ Update state with ステータスが延期になること

Time: 00:00.262, Memory: 34.01 MB

OK (4 tests, 16 assertions)

GETでの更新は問題

本来なら、テストをクリアして完成となるはずですが、このプログラムは http メソッドの GET で更新しています。 思い出してください。GET では情報の要求のみで、データの更新はご法度でした。 データの更新をするには PUT メソッドを使います。