既存のDBのそれぞれのテーブルにおいて、migrationファイルやfactoryファイルが揃ったところで、phpunitのテストを作成して実行してみましょう。

テストDBの作成

テストDBは、phpunitのためだけのDBとなるゆえに、ブラウザでアクセスする本DBとは違うDBを作成します。つまり、開発サイトでは、プロジェクト1つに対して2つのDBを持つことになります。

本DBは、mysqlで、テストのDBには、sqliteという設定がドキュメントでも一般のLaravelの記事でも書かれていますが、私はテストといえども、本DBもテストDBも同じmysqlを使用することを勧めます。条件をまったく同じとしたいからです。

まず、mysqlのコマンドで、テストDB(demo_test)を作成します。

$ echo "create database demo_test" | mysql -u root -p
Query OK, 1 row affected (0.00 sec)

次に、config/database.phpをエディターで開いて、mysqlのコネクションをコピーして、mysql_testのコネクションを作成します。

use Illuminate\Support\Str;

return [
....
    'connections' => [
...
        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

        'mysql_test' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => 'demo_test',
            'username' => 'root',
            'password' => 'secret',
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],
...

mysql_testにおいて編集した項目は、database、username、passwordです。env()を使用せずに、固定としています。必要なら他の項目も同様に編集してください。それから、usernameに指定するmysqlのユーザーは必ず、drop table, create table, truncateのsql文と実行できる権限を持つユーザーである必要があります。それゆえに、ここではrootのmysqlユーザーを使用しています。

次に、テーブル作成のartisanコマンドを実行して、テストDBにテーブルを作成します。前回で作成したmigrationファイルがここで役立ちます。

$ php artisan migrate:fresh --database=mysql_test
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.02 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.01 seconds)

必ず、–databasemysql_testのコネクションを指定ください。指定しないと、本DBの方が空となってしまうので注意を!

phpunitの設定

テストDBをテストの実行で使用するには、phpunit.xmlの編集が必要です。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>

        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <server name="DB_CONNECTION" value="mysql_test"/>
        <server name="MAIL_DRIVER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
    </php>
</phpunit>


DB_CONNECTION
mysql_testと指定しています。

さて、これでphpunitを実行できます。vendor/bin/phpunitは、このプロジェクトでインストールされたバージョンのphpunitなので、必ずそのパスを使用してください。

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

..                                                                  2 / 2 (100%)

Time: 335 ms, Memory: 16.00 MB

OK (2 tests, 2 assertions)

OKがテスト成功、という意味です。

RefreshDatabaseトレイト

先のphpunitの実行では、データベースのテストは含まれていません。作成してみましょう。

$php artisan make:test --unit UserTest
Test created successfully.

この実行で、tests/Unit/UserTest.phpのファイルが作成されますが、それを以下のように編集します。

namespace Tests\Unit;

// use PHPUnit\Framework\TestCase // Laravel 6.xで生成されるが、コメントして以下を追加
use Tests\TestCase;

use Illuminate\Foundation\Testing\RefreshDatabase; // 追加

use App\User;

class UserTest extends TestCase
{
    use RefreshDatabase;

    public function test_create_a_user()
    {
        factory(User::class)->create(); // usersのDBテーブルにレコードを1つ作成

        $this->assertCount(1, User::all()); // すべてのレコードを取得してレコード数が1であることを確認
    }
}

実行してみましょう。

$ vendor/bin/phpunit --filter UserTest
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 511 ms, Memory: 22.00 MB

OK (1 test, 1 assertion)

成功ですね。

このテストは何回実行しても成功となります。

$ vendor/bin/phpunit --filter UserTest
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 511 ms, Memory: 22.00 MB

OK (1 test, 1 assertion)
$ vendor/bin/phpunit --filter UserTest
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 511 ms, Memory: 22.00 MB

OK (1 test, 1 assertion)

試しに、use RefershDatabaseをコメントして実行してみてください。

$ vendor/bin/phpunit --filter UserTest
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 511 ms, Memory: 22.00 MB

OK (1 test, 1 assertion)

$ vendor/bin/phpunit --filter UserTest
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 324 ms, Memory: 18.00 MB

There was 1 failure:

1) Tests\Unit\UserTest::testExample
Failed asserting that actual size 2 matches expected size 1.

/vol1/usr/www/repos/repos/l6x/tests/Unit/UserTest.php:23

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

1回名は成功ですが、2回目は失敗です.なぜなら、RefereshDatabaseなしではレコードは追加されていくばかりだからです。RefereshDatabaseのトレイトを使用することによりDBテーブルが空となり、毎回同じ条件でテストを実行できます。

By khino