PHP
laravel

Laravel の Real Time Facade で簡単にテストする

概要

Real Time Facade が便利だったけどあまりそれについての tips とかがなかったので書いてみる.
テストしにくいような処理を Real Time Facade を用いてサクッとテストかけるよっていう紹介.

準備

Qiita の投稿一覧を API で取得して、そのタイトルを一覧するような処理を書く.
多分 QiitaController みたいな感じでこうなる

# Controller 作成
php artisan make:controller QiitaController

コントローラーの中身はこんな感じ

<?php // app/Http/Controllers/QiitaController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class QiitaController extends Controller
{
    public function index(Request $request)
    {
        $page = $request->input('page', 1);
        $perPage = $request->input('per_page', 20);
        $jsonResponse = file_get_contents("https://qiita.com/api/v2/items?page={$page}&per_page={$perPage}");
        $response = json_decode($jsonResponse, true);

        $str = '<ul>';
        foreach ($response as $item) {
            $str .= '<li>' . $item['title'] . '</li>';
        }
        $str .= '</ul>';
        return $str;
    }
}

テスト書いてみる

php artisan make:test QiitaControllerTest

多分中身はこんな感じ

<?php

namespace Tests\Feature;

use Tests\TestCase;

class QiitaControllerTest extends TestCase
{
    public function testIndex()
    {
        $response = $this->get('/qiita');
        $response->assertStatus(200);
    }
}

とりあえずこれでテストは通る。
これでもいいけど、これはQiitaのサイトのステータスやネットワークの状態によってテストの結果が変わってしまう。
もう少しかっこよくしたい。

処理のまとまりを切り出す

方針としては、API取得の部分を関数に切り出すと良さそう。
仮に QiitaApi というクラスに切り出してみよう。

<?php // QiitaApi

namespace App;

class QiitaApi
{
    public function getItems($page = 1, $perPage = 20)
    {
        $jsonResponse = file_get_contents("https://qiita.com/api/v2/items?page={$page}&per_page={$perPage}");
        $response = json_decode($jsonResponse, true);
        return $response;
    }
}

こんな感じ

このクラスを利用したコントローラーは下記のように修正する

<?php

namespace App\Http\Controllers;

use App\QiitaApi;
use Illuminate\Http\Request;

class QiitaController extends Controller
{
    public function index(Request $request)
    {
        $api = new QiitaApi();
        $page = $request->input('page', 1);
        $perPage = $request->input('per_page', 20);
        $response = $api->getItems($page, $perPage);

        $str = '<ul>';
        foreach ($response as $item) {
            $str .= '<li>' . $item['title'] . '</li>';
        }
        $str .= '</ul>';
        return $str;
    }
}

Facade を導入する

少し良くなったけど、まだテストし辛いのは変わらない。
ここで本題の Read Time Facade を導入して書いてみる。
5.4から導入された Real Time Facade は、use する namespace のアタマに Facades とつければそのクラスを Facade として利用できるというものらしい。
この黒魔術は localdisk さんが記事を書いているのでそちらを参照されたいhttp://tech.innovator.jp.net/entry/2017/08/29/135714

<?php

namespace App\Http\Controllers;

use Facades\App\QiitaApi;
use Illuminate\Http\Request;

class QiitaController extends Controller
{
    public function index(Request $request)
    {
        $page = $request->input('page', 1);
        $perPage = $request->input('per_page', 20);
        $response = QiitaApi::getItems($page, $perPage);

        $str = '<ul>';
        foreach ($response as $item) {
            $str .= '<li>' . $item['title'] . '</li>';
        }
        $str .= '</ul>';
        return $str;
    }
}

テストでモックを使う

さて、ここまでできたらAPI取得をQiitaのステータスやネットワークの状態に依存しないように QiitaApi クラスをモックしてみよう。
テストは下記のように書くことができる。

<?php

namespace Tests\Feature;

use Facades\App\QiitaApi;
use Tests\TestCase;

class QiitaControllerTest extends TestCase
{
    public function testIndex()
    {
        QiitaApi::shouldReceive('getItems')
            ->withAnyArgs()
            ->once()
            ->andReturn([
                ['title' => 'hoge'],
                ['title' => 'fuga'],
                ['title' => 'piyo'],
            ]);
        $response = $this->get('/qiita');
        $response->assertStatus(200);
        $response->assertSeeText('hoge');
        $response->assertSeeText('fuga');
        $response->assertSeeText('piyo');
    }
}

このように Real Time Facade はそれ自体がモックを簡単にできるようにされているので、どのメソッドがコールされ、何を返すかを指定することができる。(端折ったけど、実際のレスポンスをフィクスチャとして保持して、それを返すようにしたほうがよさそう)