第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
を引数に追加するだけで良い。
引数で受け取った$request
にtitle
があるか、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.php
のtestPost()
を見てみよう。
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('更新')
で更新処理を実行
となっている。 まさに、この箇所が書き換えを行なっている。
それに対して確認の仕方が既存のデータに依存し、また更新処理の確認を考慮できていない、 というところが問題点だ。
ここで、ついに時が来たようだ。
RefreshDatabase
とDatabaseMigrations
によって、「既存のデータに依存しないテスト」へと作り変えなければいけない。
大変かもしれないが、これを乗り越えないとテストの成功を得ることは難しい。
では、次に進んでいこう。