第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でないやり方に比べれば、ここに来るまでに随分時間がかかったと感じるると思う。 ただ、一旦テストを書いたことで、「ここはテストを書き足したい」と思ったり、 「もっとやり方を変えた方が良いかな」と思ったり、と、具体的な想像がしやすくなったはずだ。

そういったときに、気軽にテストを書き足し、コードを変更できるように、このコードはすでになっているはずだ。 つまり、「きちんとしたテストを書くこと」が大事なのではなく、 「気軽にコードに触れる状態にしておくこと」を大事にする必要がある。

そのために、下記のことを意識しておいて欲しい。

  • 「自分の設計意図を確認する」テストを一緒に(できれば先に)書くこと
  • そのテストに沿った「テストできる」プロダクトコードを書くこと
  • テストの結果を「グリーン(成功)」に保つこと
  • テストの結果をベースに実装・リファクタリングを進めること

ここでやったリズムを忘れずに、先に進んでいこう。

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