第1章 タスクリスト表示
03.ビューの実装
ビューファイルの作成
データが取り出せることが確認できたので、次は画面にデータを表示させるようにしたい。
まずは、表示させるためのビューファイルを作成する。
ここについてはartisan
で作成できないので、手動で作成する必要がある。
resources/views/tasks/index.blade.php
というファイルを、下記のように作成してみる。
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<title>Tasks</title>
</head>
<body>
<div class="container">
<h2>Tasks List</h2>
<ul>
@foreach ($tasks as $task)
<li>{{ $task->title }} <input type="checkbox"
name="checkbox_{{ $task->id }}" {!! $task->executed ? 'checked="checked"' : '' !!}>
</li>
@endforeach
</ul>
</div>
</body>
</html>
ここでは難しいことはせず、タスクをシンプルに一覧表示するだけにしている。 見た目で、おおよその意味は理解できると思う。
@foreach() 〜 @endforeach
は、PHPのforeach()
と同じもの。
つまり、$tasks
という配列(またはforeach()
で処理できるIterableなオブジェクト)を受け取り、
その要素を変数$task
に格納して処理を繰り返し実行するものだ。
ビューのテスト準備
では、この画面を確認するテストを作ってみよう。
この場合はブラウザでどう表示されるか、というテストになるので、少し準備が必要だ。
Laravelでビューのテストを行なうには、Laravel Dusk
というものを使う。
まずは、ライブラリのインストールから行なおう。
$ composer require --dev laravel/dusk:^2.0
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
- Installing facebook/webdriver (1.5.0): Loading from cache
- Installing laravel/dusk (v2.0.14): Loading from cache
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/dusk
Discovered Package: laravel/tinker
Package manifest generated successfully.
成功したら、続けて次のコマンドを実行する。
$ php artisan dusk:install
Dusk scaffolding installed successfully.
これにより、ビューテスト用の基本ファイルtests/DuskTestCase.php
が作成され、ビューのテストを作成する準備が整った。
と言いたが、もう少し設定が必要だ。
テストを実行した場合、内部ではブラウザ(のレンダリングエンジン)を起動してテストを行なっている。そのため、日本語を取り扱う場合に、ブラウザも日本語モードで起動しておく必要がある。
tests/DuskTestCase.php
を、下記のように修正をしよう。
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--lang=ja_JP'
]);
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}
}
34行目の後ろに,
を追加し、35行目に日本語モードで起動するためのオプションを追加している。
ビューのテスト作成と実行
ここからビューのテスト作成に入る。こちらもartisan
コマンドで、ファイルを作成する。
$ php artisan dusk:make TasksIndexTest
Test created successfully.
これで、tests/Browser/TasksIndexTest.php
というファイルが生成された。
では、どのような内容のテストを行なえば良いだろうか。
現在までの状態で/tasks
へブラウザでアクセスしたら、seeder
で登録した「テストタスク」という文字が含まれているはずである。
今回は、ページ内に「テストタスク」という文字が含まれているかを確認するテストにしてみる。
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class TasksIndexTest extends DuskTestCase
{
/**
* Tasks Index Test.
*
* @throws \Exception
* @throws \Throwable
*/
public function testIndex()
{
$this->browse(function (Browser $browser) {
$browser->visit('/tasks')
->assertSee('テストタスク')
->screenshot("tasks_index");
});
}
}
今回はブラウザが動いていることを視覚的に見るために、screenshot()
メソッドも入れておく。
これを入れておくと、テスト実行時にtests/Browser/screenshots
ディレクトリの下へ、
スクリーンショットを保存してくれるようになる。
では、実行してみよう。
$ php artisan dusk
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.
.F 2 / 2 (100%)
Time: 8.21 seconds, Memory: 12.00MB
There was 1 failure:
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
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
当然だが、まだデータを取得する処理をコントローラに実装していないので、想定通りにテストに失敗している。
コントローラの仮実装
まずは、テストを成功させるための仮実装をやってみよう。 コントローラから、タイトルが「テストタスク」となる要素をビューに渡せば良いはずだ。
app/Http/Controllers/TaskController.php
を、下記のように修正してみる。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TaskController extends Controller
{
public function index()
{
$tasks = [
(object) ['id' => 10, 'title' => 'テストタスク', 'executed' => false],
];
return view('tasks.index', ['tasks' => $tasks]);
}
}
ビュー側では$tasks
という配列を受け取り、その要素の中のtitle
というキーに対応した値を表示している。
このビューの実装に合わせる形で$tasks
という配列とその要素を定義し、ビューへ渡してHTMLを生成するように書いた。
実際に、テストを実行して確かめてみよう。
$ php artisan dusk
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 8.57 seconds, Memory: 12.00MB
OK (2 tests, 2 assertions)
狙い通りに成功となった。 ちなみに、テスト時にスクリーンショットを撮っているので、それを確認しておこう。
Macであれば、下記のようにしてコマンドラインからプレビューアプリを起動できる。
$ open tests/Browser/screenshots/tasks_index.png
画像内に、「テストタスク」と表示されているはずだ。
これでコントローラからビューへどのようなデータを渡せば良いか、確認できた。
いよいよ大詰め、コントローラでデータを取得してビューに渡すように修正しよう。
コントローラの修正
データの取り出し方法は、TaskTest.php
を作成した時にすでに経験済みだ。
すんなりと書けるだろう。
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]);
}
}
12行目でモデルであるTask
クラスを使い、データベースに保存されている全件を取得するようにした。
これで、先ほどテストに成功した状態からデータベースを使うコードになった。
保存したら、テストを実行しよう。
$ php artisan dusk
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 6.82 seconds, Memory: 12.00MB
OK (2 tests, 2 assertions)
無事に成功した。続けて、コントローラやモデルのテストも実行しておく。(仕組み上、別々に動かす必要がある。)
$ vendor/bin/phpunit
PHPUnit 6.5.8 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 423 ms, Memory: 12.00MB
OK (2 tests, 6 assertions)
こちらも変わらず、テストに成功している。
最後に、スクリーンショットも見ておこう。
$ open tests/Browser/screenshots/tasks_index.png
一行目に長いランダム文字列のデータとなっているが、全体としてきちんと意図通りになっているはずだ。
ここまで環境構築直後の確認以降は、一度も(手動で)ブラウザを起動せずに実装・テストを行なうことができた。
最後の最後に、ブラウザでも確認してみよう。自分が標準として設定しているブラウザで、タスクの一覧が表示できるはずだ。 見た目は、先ほどのスクリーンショットと同じになっているはずである。
$ open http://localhost:9080/tasks
本章のまとめ
最初なので一気に色々とやったが、どうだっただろうか。
次に進むたびに「テストを書いて実行する」、「以前のテストが成功する」、という状況を繰り返してきた。
これは、繰り返しになるが、このドキュメントは「TDD
の手法を交えたLaravel
の開発」の流れを、
本ドキュメントの筆者と一緒に体験していく、というものである。
今までのTDD
でないやり方に比べれば、ここに来るまでに随分時間がかかったと感じるると思う。
ただ、一旦テストを書いたことで、「ここはテストを書き足したい」と思ったり、
「もっとやり方を変えた方が良いかな」と思ったり、と、具体的な想像がしやすくなったはずだ。
そういったときに、気軽にテストを書き足し、コードを変更できるように、このコードはすでになっているはずだ。 つまり、「きちんとしたテストを書くこと」が大事なのではなく、 「気軽にコードに触れる状態にしておくこと」を大事にする必要がある。
そのために、下記のことを意識しておいて欲しい。
- 「自分の設計意図を確認する」テストを一緒に(できれば先に)書くこと
- そのテストに沿った「テストできる」プロダクトコードを書くこと
- テストの結果を「グリーン(成功)」に保つこと
- テストの結果をベースに実装・リファクタリングを進めること
ここでやったリズムを忘れずに、先に進んでいこう。