【Laravel】簡易なコントローラ単位のテストをする

著者:杉浦

【Laravel】簡易なコントローラ単位のテストをする

 テストを書き、自動化できると何かと楽です。ライブラリのアップデートによって機能が壊されていないか確認するときなど全機能を総なめするので大変助かります。Laravelにおいて、あるアドレスにアクセスされた時にはあるコントローラのあるメソッドが動かされる様になっています。コントローラの各メソッドをテストすることができれば、各ページのPHPに関しての結合テストが完了します。もしAPIとして閉じているならば、これだけで全体のテストがほぼ完了します。
 コントローラのメソッドの起動と結果は大仰です。完璧なリクエストを生成するのも、モックのDB(データベース)を用意するのも手間です。この記事ではなるべく楽にまあまあの動きができるものを作ります。
 リクエストの作り方です。守るべきコード規約を一つ作ります。次の様にRequestクラスを記述し、リクエストのボディを参照するときはRequestインスタンスのプロパティから見る、というものです。

// HogeRequest.php
/**
 * @property string $id
 * @property string $name
 */
class HogeRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'id' => 'required|string',
            'name' => 'required|string',
        ];
    }
}
// FugaController.php

class FugaController extends Controller
{
    public function foo(HogeRequest $request)
    {
        $id = $request->id;            // こんな感じで取得する.get, post等のリクエスト形式を考慮しなくていいし値なしならnullが返ってくる
        $name = $request->get('name'); // この取り方をやめる
        $desc = $request->desc ?? '説明文'; // デフォルト値が欲しいならこんな感じ
    }
}

 こうするとテスト中では次のように記述するだけでテストで使うためのRequestインスタンスが出来、次のようにテストをささっと書けます。

    public function testFoo()
    {
        $request = new HogeRequest();
        $request->id = 'idの値';
        $request->name = 'nameの値';

        $response = $this->controller->foo($request);
        self::assert($expected, $response);
    }

 headerなどを見るならもう少し拡張する必要があります。
 リクエストを投げてどうこうされる処理にはDBを触るものもあります。DBの中身が変わると不都合な時もあり、テストの際にはDB中のデータを保護したいです。これを実現するのは割と簡単で全単体テスト共通の親クラスTestCaseを用意して次の様にトランザクションとロールバックを仕込むだけで完成です。ブラウザを介してDBを弄らない単体テストの範囲だからこそ簡単にできます。

<?php

namespace Tests;

use Exception;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Throwable;

/**
 * 基礎. DBの保護などの全テスト共通処理を記述する.
 * Class TestCase
 * @package Tests
 */
abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    /**
     * @throws BindingResolutionException
     * @throws Exception
     */
    public function setUp(): void
    {
        parent::setUp();
        $this->app->make('db')->beginTransaction();
    }

    /**
     * @throws BindingResolutionException
     * @throws Throwable
     */
    public function tearDown(): void
    {
        $this->app->make('db')->rollBack();
        parent::tearDown();
    }
}

 もちろんメールやらSlackやらLINEやら色々とこの記事のやり方に納まらないものもありますが結構な範囲をカバーできて便利です。

  • この記事いいね! (1)

著者について

杉浦 administrator