第3章 タスクの更新と完了

04.コントローラの実装

テストの修正

先に書いたコントローラのテストだが、パスの確認のテストなので少々物足りない。 データの更新を行なう処理なので、実際にデータ更新を確認できるテストにしよう。

tests/Feature/TaskControllerTest.phpを、下記のように修正する。





 






 







 
 
 
 
 
 
 
 
 
 
 
 
 
 






 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class TaskControllerTest extends TestCase
{
    use DatabaseTransactions;

()
    /**
     * Put Task Detail Path Test
     *
     * @return void
     */
    public function testPutTaskPath()
    {
        $data = [
            'title' => 'test title',
        ];
        $this->assertDatabaseMissing('tasks', $data);

        $response = $this->put('/tasks/1', $data);

        $response->assertStatus(302)
            ->assertRedirect('/tasks/1');

        $this->assertDatabaseHas('tasks', $data);
    }

    /**
     * Put Task Detail Path Test 2
     *
     * @return void
     */
    public function testPutTaskPath2()
    {
        $data = [
            'title' => 'テストタスク2',
            'executed' => true,
        ];
        $this->assertDatabaseMissing('tasks', $data);

        $response = $this->put('/tasks/2', $data);

        $response->assertStatus(302)
            ->assertRedirect('/tasks/2');

        $this->assertDatabaseHas('tasks', $data);
    }
}

今回は少々長いが、順番に見ていこう。

testPutTaskPath()

まずは、testPutTaskPath()から。

$dataに更新したい値を格納している。 今回は'title'のみを更新する、ということになる。

その次の$this->assertDatabaseMissing('tasks', $data);だが、 これはデータベースの'tasks'テーブルの中に$dataに相当するレコードが無いことを確認している。

更新前にはこのデータが存在していない、という確認だ。

$this->put('/tasks/1', $data)で更新処理が実行され、 レスポンスとしてはリダイレクト処理が行なわれる。

assertStatus(302)の確認は前回も記載していたが、 リダイレクト先のURL確認としてassertRedirect('/tasks/1')を追加した。

リダイレクト先は、更新対象の詳細ページと同じなので、それを確認することになる。

ここまで終わればデータは更新されているはずなので、 今度は$this->assertDatabaseHas('tasks', $data);によって 'tasks'テーブルの中に$dataに相当するレコードがあることを確認している。

testPutTaskPath2()

次にtestPutTaskPath2()の中を見てみよう。

こちらも確認の流れはtestPutTaskPath()の時と同じだ。 違いは$dataにて、'title''executed'の両方を設定していることだ。

テスト実行

期待としては、$dataの違いによらずどちらもきちんと更新処理が行なわれて欲しい。 まずは、テストを実行してみよう。

$ vendor/bin/phpunit
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.

...FF....                                                           9 / 9 (100%)

Time: 866 ms, Memory: 16.00MB

There were 2 failures:

1) Tests\Feature\TaskControllerTest::testPutTaskPath
Failed asserting that a row in the table [tasks] matches the attributes {
    "title": "test title"
}.

Found: [
    {
        "id": 1,
        "title": "TJcwTQcA6AvMB7Jr7p1lLa2B9htIpSC7civt7KfEPmgMpABfvmfOUJ0nN6pDbwqEnSz1dBrkoAYedem5xNjASxMAlwsgyDCJNKzx0jOis3WKEdEXBJxFbmJAxNPlB2RF0R2wV8B7UkSBrb2ZpT3EkbHvjIk15fyAc0En2IS1S7iWGDOGJ0ZjiTsy6mHdX9FDXfrDCbNW4FVS7ldTy5rbu4sZMSCARnMf0eHta9VKTwj83n6lZdqxUREJvg8hZXLYkgNYStKtNmxnWVzwnfMF57AIDNuRL9HVBp2EiTkTUT5Rzy2b7vg0LlSXERVu7btyVXA12eEwdSCDU62mz6JPznhuHDXqyY9zM5OU06O7kEZvwTzLd6aZCXdmb20updeuTMR2nY5qnivVDTIhG0FuGT4556rVEZE13MudxFm9olanq08LXzIseCa8BNc7GUQ9J9t4BnwwGZxyCyc0yQTER357rNtCejerrjFnifRUxWCqTR2eQvLAeJUj1HIYgyCG",
        "executed": 0,
        "created_at": null,
        "updated_at": null
    },
    {
        "id": 2,
        "title": "\u30c6\u30b9\u30c8\u30bf\u30b9\u30af",
        "executed": 0,
        "created_at": null,
        "updated_at": null
    },
    {
        "id": 3,
        "title": "\u7d42\u4e86\u30bf\u30b9\u30af",
        "executed": 1,
        "created_at": null,
        "updated_at": null
    }
].

/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:22
/Users/[ユーザ名]/task-manager/task-manager/tests/Feature/TaskControllerTest.php:67

2) Tests\Feature\TaskControllerTest::testPutTaskPath2
Failed asserting that a row in the table [tasks] matches the attributes {
    "title": "\u30c6\u30b9\u30c8\u30bf\u30b9\u30af2",
    "executed": true
}.

Found: [
    {
        "id": 1,
        "title": "TJcwTQcA6AvMB7Jr7p1lLa2B9htIpSC7civt7KfEPmgMpABfvmfOUJ0nN6pDbwqEnSz1dBrkoAYedem5xNjASxMAlwsgyDCJNKzx0jOis3WKEdEXBJxFbmJAxNPlB2RF0R2wV8B7UkSBrb2ZpT3EkbHvjIk15fyAc0En2IS1S7iWGDOGJ0ZjiTsy6mHdX9FDXfrDCbNW4FVS7ldTy5rbu4sZMSCARnMf0eHta9VKTwj83n6lZdqxUREJvg8hZXLYkgNYStKtNmxnWVzwnfMF57AIDNuRL9HVBp2EiTkTUT5Rzy2b7vg0LlSXERVu7btyVXA12eEwdSCDU62mz6JPznhuHDXqyY9zM5OU06O7kEZvwTzLd6aZCXdmb20updeuTMR2nY5qnivVDTIhG0FuGT4556rVEZE13MudxFm9olanq08LXzIseCa8BNc7GUQ9J9t4BnwwGZxyCyc0yQTER357rNtCejerrjFnifRUxWCqTR2eQvLAeJUj1HIYgyCG",
        "executed": 0,
        "created_at": null,
        "updated_at": null
    },
    {
        "id": 2,
        "title": "\u30c6\u30b9\u30c8\u30bf\u30b9\u30af",
        "executed": 0,
        "created_at": null,
        "updated_at": null
    },
    {
        "id": 3,
        "title": "\u7d42\u4e86\u30bf\u30b9\u30af",
        "executed": 1,
        "created_at": null,
        "updated_at": null
    }
].

/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:22
/Users/[ユーザ名]/task-manager/task-manager/tests/Feature/TaskControllerTest.php:83

FAILURES!
Tests: 9, Assertions: 23, Failures: 2.

結果は長いが、テストの失敗箇所はどちらも$this->assertDatabaseHas('tasks', $data);の部分だ。 つまり、更新処理が正しく行なわれていないことがわかる。

コントローラ実装

では、いよいよ正しく更新されるように、プロダクトコードを実装しよう。 テストを書いた通り、$dataで渡されたパラメータは、どちらか一方だけでも、または両方でも、 受け取ったものを更新対象とするようにしたい。

これは、app/Http/Controllers/TaskController.phpを下記のように書けば良いだろう。



























 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


<?php

namespace App\Http\Controllers;

use App\Task;
use Illuminate\Http\Request;

class TaskController extends Controller
{
    public function index()
    {
        $tasks = Task::all();

        return view('tasks.index', ['tasks' => $tasks]);
    }

    public function detail(int $id)
    {
        $task = Task::find($id);
        if ($task === null) {
            abort(404);
        }

        return view('tasks.detail', ['task' => $task]);
    }

    public function update(int $id, Request $request)
    {
        $task = Task::find($id);
        if ($task === null) {
            abort(404);
        }

        $fillData = [];
        if (isset($request->title)) {
            $fillData['title'] = $request->title;
        }
        if (isset($request->executed)) {
            $fillData['executed'] = $request->executed;
        }

        if (count($fillData) > 0) {
            $task->fill($fillData);
            $task->save();
        }

        return redirect('/tasks/' . $id);
    }
}

27行目、update()の引数に突然Request $requestというものが追加された。 これは、Laravelがパラメータをうまく処理して引数に渡してくれるものだ。 実装する側としては、コントローラでパラメータを受け取りたい場合には、 このように難しいことを考えずにRequest $requestを引数に追加するだけで良い。

引数で受け取った$requesttitleがあるか、executedがあるかを確認し、 あればそれぞれ更新対象としている。

更新する内容があれば、$task->fill($fillData)でオブジェクトに格納し、 $task->save()でレコードに反映を行なっている。

最後は詳細画面へリダイレクト、としてここは終了だ。

テストを再度実行

さて、コントローラを実装できたので、もう一度テストを実行しよう。

$ vendor/bin/phpunit
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.

.........                                                           9 / 9 (100%)

Time: 858 ms, Memory: 16.00MB

OK (9 tests, 23 assertions)

無事に成功となった。 テストで仕様を考えて、その通りにプロダクトコードを実装する、 という形で書くことができた。

引き続き、ビューテストも実行しよう。

$ php artisan dusk
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.

..FF                                                                4 / 4 (100%)

Time: 15.24 seconds, Memory: 14.00MB

There were 2 failures:

1) Tests\Browser\TasksIndexTest::testIndex
Did not see expected text [テストタスク] within element [body].
Failed asserting that false is true.

/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/dusk/src/Concerns/MakesAssertions.php:348
/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/dusk/src/Concerns/MakesAssertions.php:319
/Users/[ユーザ名]/task-manager/task-manager/tests/Browser/TasksIndexTest.php:21
/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:67
/Users/[ユーザ名]/task-manager/task-manager/tests/Browser/TasksIndexTest.php:23

2) Tests\Browser\TasksIndexTest::testIndexToDetail
Did not see expected link [テストタスク] within [body].
Failed asserting that false is true.

/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/dusk/src/Concerns/MakesAssertions.php:423
/Users/[ユーザ名]/task-manager/task-manager/tests/Browser/TasksIndexTest.php:34
/Users/[ユーザ名]/task-manager/task-manager/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:67
/Users/[ユーザ名]/task-manager/task-manager/tests/Browser/TasksIndexTest.php:39

FAILURES!
Tests: 4, Assertions: 5, Failures: 2.

むむ、失敗だ。 さっきまで問題なかったはずなのに・・・。

メッセージをきちんと読んでみると、テストタスクという文字がブラウザ上に表示されなくなっており、 そのためにテストの失敗になっているようだ。

先ほどコントローラを実装して正しく更新処理が行なわれるようになった結果、 テストタスクという文字は無くなったわけだ。

つまり、先ほどまで書いていたビューテストでは更新に対応できておらず、 本来の仕様と異なる誤ったテストだった、、、ということだ。

tests/Browser/TaskDetailTest.phptestPost()を見てみよう。





 
 

 






    public function testPost()
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/tasks/2')
                ->assertInputValue('#title', 'テストタスク')
                ->type('#title', 'test task')
                ->screenshot('task_post_typed')
                ->press('更新')
                ->pause(1000)
                ->assertPathIs('/tasks/2')
                ->screenshot('task_post_pressed');
        });
    }
  • assertInputValue('#title', 'テストタスク')でテキストボック内のタイトルを確認し
  • type('#title', 'test task')でテキストボックス内のタイトルを変更し
  • press('更新')で更新処理を実行

となっている。 まさに、この箇所が書き換えを行なっている。

それに対して確認の仕方が既存のデータに依存し、また更新処理の確認を考慮できていない、 というところが問題点だ。

ここで、ついに時が来たようだ。 RefreshDatabaseDatabaseMigrationsによって、「既存のデータに依存しないテスト」へと作り変えなければいけない。 大変かもしれないが、これを乗り越えないとテストの成功を得ることは難しい。

では、次に進んでいこう。

Last Updated (JST): 7/7/2019, 2:32:08 PM