LoginSignup
32
23

More than 3 years have passed since last update.

Laravel PHPUnitのdataProviderでFacadeやfactory等のヘルパ関数を使う方法

Last updated at Posted at 2019-08-31

追記: 2020/08/12 Laravel6.0以降はこの方法だとエラーになるため、遅延評価をお試しください。
参考になる記事をご紹介します。

問題のテストコード

<?php

namespace Tests\Unit;

use App\User;
use Hash;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * @param string $name
     * @param array $data
     * @param string $dataName
     */
    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);
        $this->createApplication();
    }

    /**
     * @param User $user
     * @param string $hash
     * @return void
     * @dataProvider dataProvider
     */
    public function testBasicTest(User $user, string $hash): void
    {
        $this->assertNotNull($user);
        $this->assertNotNull($hash);
    }

    /**
     * @return array
     */
    public function dataProvider(): array
    {
        $data = [];
        foreach (range(1, 100) as $i) {
            $data["TestCase$i"] = [
                'user' => factory(User::class)->create(),
                'hash' => Hash::make('secret'),
            ];
        }

        return $data;
    }
}
$ ./vendor/bin/phpunit
PHPUnit 7.5.15 by Sebastian Bergmann and contributors.

W                                                                   1 / 1 (100%)

Time: 323 ms, Memory: 6.00 MB

There was 1 warning:

1) Warning
The data provider specified for Tests\Unit\ExampleTest::testBasicTest is invalid.
Unable to locate factory with name [default] [App\User].

WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.

Warningが表示されます。

原因

PHPUnitが任意のテストケースでデータプロバイダーとメソッドの呼び出し順序に起因する問題のようです。

解決方法1: __constructにcreateApplicationを追加する

    /**
     * @param string $name
     * @param array $data
     * @param string $dataName
     */
    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);
        $this->createApplication();
    }

コンストラクタをオーバーライドして、 $this->createApplication(); を追記してあげればokです。
これで dataProvider 内でファサードやヘルパ関数を呼び出せます。

[推奨] 解決方法2: dataProviderにcreateApplicationを追加する

    /**
     * @return array
     */
    public function dataProvider(): array
    {
        $this->createApplication();

        $data = [];
        foreach (range(1, 100) as $i) {
            $data["TestCase$i"] = [
                'user' => factory(User::class)->create(),
                'hash' => Hash::make('secret'),
            ];
        }

        return $data;
    }

解決方法1 と 解決方法2 でテスト実行

解決方法1でテスト実行

$ ./vendor/bin/phpunit
PHPUnit 7.5.15 by Sebastian Bergmann and contributors.

...............................................................  63 / 100 ( 63%)
.....................................                           100 / 100 (100%)

Time: 18.16 seconds, Memory: 46.00 MB

OK (100 tests, 200 assertions)

解決方法2でテスト実行

$ ./vendor/bin/phpunit
PHPUnit 7.5.15 by Sebastian Bergmann and contributors.

...............................................................  63 / 100 ( 63%)
.....................................                           100 / 100 (100%)

Time: 8.67 seconds, Memory: 22.00 MB

OK (100 tests, 200 assertions)

解決方法1 と 解決方法2 でテスト結果

解決方法1: Time: 18.16 seconds, Memory: 46.00 MB
解決方法2: Time: 8.67 seconds, Memory: 22.00 MB

100件のテストデータで10秒、メモリ量は2倍の差が出ました。
テストケースが多い場合はテストの初期化でテストの実行速度に明らかな差が出るので解決方法2を推奨します。

補足: createApplication

tests/CreatesApplication.php Laravelが元々用意しているトレイトの関数を呼び出してます。
Laravelの初期化処理を行われるので、Facadeが読み込めるようになります。

<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;

trait CreatesApplication
{
    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Kernel::class)->bootstrap();

        return $app;
    }
}
32
23
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
23