Help us understand the problem. What is going on with this article?

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

問題のテストコード

<?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;
    }
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away