このパッケージは、Swoole を使用して時間/IO 関連の単体テスト (スリープ関数呼び出し、データベース クエリ、API 呼び出しなど) をより速く実行するのに役立ちます。
パッケージcounit を使用すると、Swoole を使用して単一の PHP プロセス内で複数の時間/IO 関連テストを同時に実行できます。 Counit はPHPUnitと互換性があります。これは次のことを意味します。
counitの典型的なテスト ケースは次のようになります。
use Deminy Counit TestCase ; // Here is the only change made for counit, comparing to test cases for PHPUnit.
class SleepTest extends TestCase
{
public function testSleep (): void
{
$ startTime = time ();
sleep ( 3 );
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
}
}
PHPUnitと比較すると、 counit はテスト ケースを高速化できます。実際のプロジェクトでPHPUnitとcounit を使用して同じテスト スイートを実行した場合の比較を次に示します。テスト スイートでは、多くのテストがDeminyCounitCounit::sleep()メソッドを呼び出して、何かが起こるのを待ちます (たとえば、データの有効期限が切れるのを待ちます)。
テストの数 | アサーションの数 | 終了までの時間 | |
---|---|---|---|
counit (Swoole なし)、または PHPUnit | 44 | 1148 | 9分18秒 |
counit (Swoole が有効な場合) | 19秒 |
パッケージはComposer を使用してインストールできます。
composer require deminy/counit --dev
または、 composer.jsonファイルにパッケージdeminy/counitが含まれていることを確認してください。
{
"require-dev" : {
"deminy/counit" : " ~0.2.0 "
}
}
フォルダー ./tests/unit/global および ./tests/unit/case-by-case にはいくつかのサンプル テストが含まれており、そこには次の時間関連のテストが含まれています。
サンプル テストを実行するには、まず Docker コンテナを起動し、Composer パッケージをインストールしてください。
docker-compose up -d
docker compose exec -ti swoole composer install -n
PHP コンテナ、Swoole コンテナ、Redis コンテナ、MySQL コンテナ、および Web サーバーの 5 つのコンテナが起動されています。 PHP コンテナには Swoole 拡張機能がインストールされていませんが、Swoole コンテナにはインストールされて有効になっています。
前に述べたように、テスト ケースはPHPUnitの場合と同じ方法で作成できます。ただし、 counitを使用して時間/IO 関連のテストをより高速に実行するには、それらのテスト ケースを作成するときにいくつかの調整を行う必要があります。これらの調整は 2 つの異なるスタイルで行うことができます。
このスタイルでは、各テスト ケースが別のコルーチンで自動的に実行されます。
このスタイルで記述されたテスト ケースの場合、既存のテスト ケースに加えるべき唯一の変更は、基本クラスとしてPHPUnitFrameworkTestCaseの代わりにクラスDeminyCounitTestCase を使用することです。
グローバル スタイルの典型的なテスト ケースは次のようになります。
use Deminy Counit TestCase ; // Here is the only change made for counit, comparing to test cases for PHPUnit.
class SleepTest extends TestCase
{
public function testSleep (): void
{
$ startTime = time ();
sleep ( 3 );
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
}
}
カスタマイズされたメソッドsetUpBeforeClass()およびTearDownAfterClass()がテスト ケースで定義されている場合は、これらのカスタマイズされたメソッド内でそれらの親メソッドを適宜呼び出すようにしてください。
このスタイルは、テスト ケースに即時アサーションがないこと、また sleep() 関数呼び出しやコルーチンに適した IO 操作の前にアサーションがないことを前提としています。次のようなテスト ケースは引き続き機能しますが、テストするといくつかの警告メッセージが表示されます。
class GlobalTest extends Deminy Counit TestCase
{
public function testAssertionSuppression (): void
{
self :: assertTrue ( true , ' Trigger an immediate assertion. ' );
// ......
}
}
「ケースバイケース」スタイル (次のセクションで説明) を使用してこのテスト クラスを書き直すと、警告メッセージが表示されなくなります。
このスタイルで記述されたテストをさらに見つけるには、フォルダー ./tests/unit/global (テスト スイート「グローバル」) の下のテストを確認してください。
このスタイルでは、テスト ケースを直接変更して、テスト ケースを非同期に動作させます。
このスタイルで記述されたテスト ケースの場合、PHP の実行を待機する必要があるテスト ケースや IO 操作を実行する必要があるテスト ケースでは、それに応じてクラスDeminyCounitCounit を使用する必要があります。通常、次のメソッド呼び出しが使用されます。
ケースバイケース スタイルの典型的なテスト ケースは次のようになります。
use Deminy Counit Counit ;
use PHPUnit Framework TestCase ;
class SleepTest extends TestCase
{
public function testSleep (): void
{
Counit:: create ( function () { // To create a new coroutine manually to run the test case.
$ startTime = time ();
Counit:: sleep ( 3 ); // Call this method instead of PHP function sleep().
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
});
}
}
「このテストはアサーションを実行しませんでした」という警告メッセージを抑制する必要がある場合、またはアサーションの数を一致させる必要がある場合は、新しいコルーチンの作成時に 2 番目のパラメーターを含めることができます。
use Deminy Counit Counit ;
use PHPUnit Framework TestCase ;
class SleepTest extends TestCase
{
public function testSleep (): void
{
Counit:: create ( // To create a new coroutine manually to run the test case.
function () {
$ startTime = time ();
Counit:: sleep ( 3 ); // Call this method instead of PHP function sleep().
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
},
1 // Optional. To suppress warning message "This test did not perform any assertions", and to make the counters match.
);
}
}
このスタイルで記述されたテストをさらに見つけるには、フォルダー ./tests/unit/case-by-case (テスト スイート「ケースバイケース」) の下にあるテストを確認してください。
ここでは、Swoole の有無にかかわらず、さまざまな環境でテストを実行します。
#1
PHPUnitを使用してテスト スイートを実行します。
# To run test suite "global":
docker compose exec -ti php ./vendor/bin/phpunit --testsuite global
# or,
docker compose exec -ti swoole ./vendor/bin/phpunit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti php ./vendor/bin/phpunit --testsuite case-by-case
# or,
docker compose exec -ti swoole ./vendor/bin/phpunit --testsuite case-by-case
#2
counitを使用してテスト スイートを実行します (Swoole は使用しません)。
# To run test suite "global":
docker compose exec -ti php ./counit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti php ./counit --testsuite case-by-case
#3
counit を使用してテスト スイートを実行します (拡張機能 Swoole を有効にします)。
# To run test suite "global":
docker compose exec -ti swoole ./counit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti swoole ./counit --testsuite case-by-case
最初の 2 つのコマンド セットは、完了するまでにほぼ同じ時間がかかります。最後のコマンド セットはcounit を使用し、Swoole コンテナー (Swoole 拡張機能が有効になっている) で実行されます。したがって、他のものよりも高速です。
スタイル | テストの数 | アサーションの数 | 終了までの時間 | |
---|---|---|---|---|
counit (Swoole なし)、または PHPUnit | グローバル | 16 | 24 | 48秒 |
ケースバイケース | 48秒 | |||
counit (Swoole が有効な場合) | グローバル | 7秒 | ||
ケースバイケース | 7秒 |
このパッケージでは複数のテストを同時に実行できるため、異なるテストで同じリソースを使用しないでください。そうしないと、競合状態が発生する可能性があります。たとえば、複数のテストで同じ Redis キーが使用されている場合、一部のテストが時折失敗する可能性があります。この場合、異なるテストケースでは異なる Redis キーを使用する必要があります。メソッドDeminyCounitHelper::getNewKey()およびDeminyCounitHelper::getNewKeys() を使用して、ランダムで一意のテスト キーを生成できます。
このパッケージは、関数呼び出しsleep() を使用するテストに最適に機能します。また、制限が適用されますが、一部の IO 関連テストをより高速に実行するのにも役立ちます。このパッケージの制限事項のリストは次のとおりです。
サンプル テストを実行するための事前構築済みイメージ deminy/counit があります。イメージを構築するコマンドは次のとおりです。
docker build -t deminy/counit:php-only -f ./dockerfiles/php/Dockerfile .
docker build -t deminy/counit:swoole-enabled -f ./dockerfiles/swoole/Dockerfile .
このパッケージを使用すると、Swoole を使用して、マルチプロセスを使用せずに複数の時間/IO 関連のテストを実行できます。つまり、すべてのテストが単一の PHP プロセス内で実行できます。それがどのように正確に機能するかを理解するには、この無料のオンライン トーク「PHP での CSP プログラミング」を確認することをお勧めします (スライドはこちら)。
PHP エコシステムには単体テストを並行して実行するための他のオプションがありますが、ほとんどは最終的にマルチプロセッシングを使用します。
MITライセンス。