PHPUnitのテスト分割と並列実行(ParaTest)を試してみた
この記事について
- PHPUnitの分割実行の導入・簡単な検証をしました
- 導入の流れや、検証中にハマったポイントなどについてまとめています
使用マシン/ツール
macOS Ventura ver13.5.2
Docker Desktop ver4.17.0
Laravel Framework 10.21.0
ParaTest v7.2.6
ベースにした環境(docker-laravel)
インストールするとPhpUnitを含めた環境が構築できるのでこちらを使用しました。
PHPUnit分割実行のプラグイン(ParaTest)
導入手順
1. docker-laravelをインストール
ドキュメントどおりにインストールを実行します。
http://localhost/ が見れれば大丈夫です。
2. ParaTestをインストール
下記コマンドを実行します
docker compose exec app composer require brianium/paratest --dev
インストールはこれだけです。
コマンド実行
ParaTestの実行は、PHPUnit実行コマンドに --parallel
をつけるだけで実行できます。
試しに実行してみます。
(--processes=2
は並列実行するプロセス数を指定するオプションで、デフォルト値はマシンによって変わるようです)
$ docker compose exec app php artisan test --parallel --processes=2
ParaTest v7.2.6 upon PHPUnit 10.3.3 by Sebastian Bergmann and contributors.
Processes: 2
Runtime: PHP 8.1.23
Configuration: /workspace/phpunit.xml
.. 2 / 2 (100%)
Time: 00:02.317, Memory: 22.00 MB
OK (2 tests, 2 assertions)
実行できました。
追加でテストコードを作成して実行してみます。
/tests/Unit/ExampleTest.php
を元に、Parallel1Test
とParallel2Test
を下記内容で作成します。
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class Parallel1Test extends TestCase
{
public function test_that_true_is_true1(): void
{
$this->assertTrue(true);
}
public function test_that_true_is_true2(): void
{
$this->assertTrue(true);
}
public function test_that_true_is_true3(): void
{
$this->assertTrue(true);
}
public function test_that_true_is_true4(): void
{
$this->assertTrue(true);
}
public function test_that_true_is_true5(): void
{
$this->assertTrue(true);
}
}
$ docker compose exec app php artisan test --parallel --processes=2
ParaTest v7.2.6 upon PHPUnit 10.3.3 by Sebastian Bergmann and contributors.
Processes: 2
Runtime: PHP 8.1.23
Configuration: /workspace/phpunit.xml
............ 12 / 12 (100%)
Time: 00:02.096, Memory: 22.00 MB
OK (12 tests, 12 assertions)
追加した分だけ実行できているようです。
ちなみに通常実行した場合は下記のようになりました。
$ docker compose exec app php artisan test
PASS Tests\Unit\ExampleTest
✓ that true is true 0.02s
PASS Tests\Unit\Parallel1Test
✓ that true is true1 0.01s
✓ that true is true2
✓ that true is true3
✓ that true is true4
✓ that true is true5
PASS Tests\Unit\Parallel2Test
✓ that true is true1
✓ that true is true2
✓ that true is true3
✓ that true is true4
✓ that true is true5
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response 0.54s
Tests: 12 passed (12 assertions)
Duration: 0.80s
テストの中身が軽いので、通常の方が早く実行されてしまうようです。
DBを使用した実行検証
次にDBも使用したテストを作ってみます。
今回は既存のUserテーブルにレコードを作成し、テスト側で呼び出すようにしてみます。
テスト実行時にテストユーザーを作成、テスト終了時にテストユーザーを削除するため、/tests/TestCase.php
に下記を追記します。
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
+ protected User $testUser;
+ protected function setUp(): void
+ {
+ parent::setUp();
+ /** @var User $testUser */
+ $this->testUser = User::query()->create([
+ "name" => "test",
+ "email" => "",
+ "password" => "test-password",
+ ]);
+ }
+ protected function tearDown(): void
+ {
+ User::query()->truncate();
+ parent::tearDown();
+ }
}
次に、Parallel1Test.php
とParallel2Test.php
を下記のように変更します。
- use PHPUnit\Framework\TestCase;
+ use Tests\TestCase;
class Parallel1Test extends TestCase
{
+ use DatabaseTransactions;
public function test_that_true_is_true1(): void
{
// 以降のメソッドも同様に変更
- $this->assertTrue(true);
+ $this->assertEquals(
+ $this->testUser->name,
+ User::find($this->testUser->id)->name
+ );
}
ここで一度実行してみます。
$ docker compose exec app php artisan test --parallel --processes=2
ParaTest v7.2.6 upon PHPUnit 10.3.3 by Sebastian Bergmann and contributors.
Processes: 2
Runtime: PHP 8.1.23
Configuration: /workspace/phpunit.xml
.......EEEEE 12 / 12 (100%)
Time: 00:02.475, Memory: 22.00 MB
There were 5 errors:
1) Tests\Unit\Parallel1Test::test_that_true_is_true1
Illuminate\Database\QueryException: SQLSTATE[42000]: Syntax error or access violation: 1044 Access denied for user 'phper'@'%' to database 'laravel_test_2' (Connection: mysql, SQL: drop database if exists `laravel_test_2`)
~省略~
Caused by
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1044 Access denied for user 'phper'@'%' to database 'laravel_test_2'
エラーが発生しました。
エラー内容:DBのユーザーに権限がない
phepr
にその権限を付与していませんでした。
mysql> SHOW GRANTS FOR 'phper'@'%';
+----------------------------------------------------+
| Grants for phper@% |
+----------------------------------------------------+
| GRANT USAGE ON *.* TO `phper`@`%` |
| GRANT ALL PRIVILEGES ON `laravel`.* TO `phper`@`%` |
+----------------------------------------------------+
試しに下記クエリでphperに権限を付与して再度テストを実行してみました。
GRANT ALL PRIVILEGES ON *.* TO 'phper'@'%';
FLUSH PRIVILEGES;
$ docker compose exec app php artisan test --parallel --processes=2
ParaTest v7.2.6 upon PHPUnit 10.3.3 by Sebastian Bergmann and contributors.
Processes: 2
Runtime: PHP 8.1.23
Configuration: /workspace/phpunit.xml
............ 12 / 12 (100%)
Time: 00:03.677, Memory: 22.00 MB
OK (12 tests, 12 assertions)
エラーなく実行できました。
テスト実行用のDBユーザーを作成してみる
これでも分割テストが実行できますが、DBユーザーに権限を付与するのではなく、テスト用のDBユーザーを作成し、テスト実行時のみテストユーザーを使うようにしてみようと思います。
今回は/docker-compose.yml
を変更し、環境構築時にテスト用のDBユーザーを作成します。
-
infra/docker/mysql/mysql_init/init.sql
を下記内容で作成します
CREATE USER 'tester'@'%' IDENTIFIED BY 'phpunittest';
GRANT ALL PRIVILEGES ON *.* TO 'tester'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
-
/docker-compose.yml
を下記のように変更します
services:
~省略~
db:
~省略~
volumes:
+ - ./infra/docker/mysql/mysql_init:/docker-entrypoint-initdb.d
~省略~
-
make remake
で環境を再構築します
この状態でテストを実行しても、先ほどと同じエラーが発生すると思います。
ここからテスト実行時のみテスト用のDBユーザーを使うようにします。
-
/src/phpunit.xml
に下記内容を追記します
~省略~
<php>
~省略~
+ <env name="DB_USERNAME" value="tester" force="true"/>
+ <env name="DB_PASSWORD" value="phpunittest" force="true"/>
</php>
</phpunit>
テストを実行してみます。
$ docker compose exec app php artisan test --parallel --processes=2
ParaTest v7.2.6 upon PHPUnit 10.3.3 by Sebastian Bergmann and contributors.
Processes: 2
Runtime: PHP 8.1.23
Configuration: /workspace/phpunit.xml
............ 12 / 12 (100%)
Time: 00:03.255, Memory: 22.00 MB
OK (12 tests, 12 assertions)
テスト時のみDBユーザーを切り替えて実行できるようになりました。
感想
まだまだ検証の余地はありますが、ParaTestに合わせてテストコードを作成していけば分割実行は可能そうですね。
業務に導入する機会があれば試してみたいと思います。
参考
Discussion