このパッケージは、内部 PHP 関数をできるだけシンプルにモックするのに役立ちます。 time()
、 str_contains()
、 rand
などの関数のモックが必要な場合は、このパッケージを使用します。
インストール
使用法
ランタイムモック
事前定義されたモック
以前の 2 つの方法の組み合わせ
PHPUユニット9
PHPUnit 10 以降
PHPUnit 拡張機能の登録
モックを登録する
州
通話の追跡
グローバル名前空間関数
内部関数の実装
内部機能
制限
データプロバイダー
コンポーザーには xepozz/internal-mocker --dev が必要です
主なアイデアは非常に単純です。PHPUnit のリスナーを登録し、最初に Mocker 拡張機能を呼び出します。
新しいファイルtests/MockerExtension.php
を作成します。
作成したファイルに次のコードを貼り付けます。
<?phpdeclare(strict_types=1);名前空間 AppTests;PHPUnitRunnerBeforeTestHook を使用;PHPUnitRunnerBeforeFirstTestHook を使用;XepozzInternalMockerMocker を使用;XepozzInternalMockerMockerState を使用;最終クラス MockerExtension は BeforeTestHook、BeforeFirstTestHook を実装します {パブリック関数executeBeforeFirstTest(): void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState::saveState(); }パブリック関数executeBeforeTest(string $test): void{ MockerState::resetState(); } }
フックをphpunit.xml.dist
に拡張機能として登録します。
<拡張機能> <extension class="AppTestsMockerExtension"/> </拡張機能>
新しいファイルtests/MockerExtension.php
を作成します。
作成したファイルに次のコードを貼り付けます。
<?phpdeclare(strict_types=1);namespace AppTests;use PHPUnitEventTestPreparationStarted;use PHPUnitEventTestPreparationStartedSubscriber;use PHPUnitEventTestSuiteStarted;use PHPUnitEventTestSuiteStartedSubscriber;use PHPUnitRunnerExtensionExtension;use PHPUnitRunnerExtensionFacade;use PHPUnitRunnerExtensionParameterCollection;PHPUnitTextUIConfigurationConfiguration を使用;XepozzInternalMockerMocker を使用;XepozzInternalMockerMockerState を使用;最終クラス MockerExtension は Extension を実装します {public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void{$facade->registerSubscribers(new class ()implements StartedSubscriber {public function Notice(Started $event): void{ MockerExtension::load(); } },新しいクラスは PreparationStartedSubscriber を実装します {public function Notice(PreparationStarted $event): void{ MockerState::resetState(); } }、 ); }パブリック静的関数load(): void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState::saveState(); } }
フックをphpunit.xml.dist
に拡張機能として登録します。
<拡張機能> <bootstrap class="AppTestsMockerExtension"/> </拡張機能>
ここでは、 ./vendor/bin/phpunit
を実行するたびに呼び出される拡張機能を登録しました。
デフォルトでは、すべての関数が生成され、 /vendor/bin/xepozz/internal-mocker/data/mocks.php
-mocker/data/mocks.php ファイルに保存されます。
Mocker
コンストラクターの最初の引数をオーバーライドしてパスを変更します。
$mocker = new Mocker('/path/to/your/mocks.php');
パッケージは、関数をモックするいくつかの方法をサポートしています。
ランタイムモック
事前定義されたモック
以前の 2 つの方法の組み合わせ
テスト ケースをモック関数で使用するようにしたい場合は、事前に登録する必要があります。
作成したMockerExtension::executeBeforeFirstTest
に戻り、 $mocks
変数を編集します。
$モック = [ ['名前空間' => 'AppService','名前' => '時間', ]、 ];
このモックは、生成されたラッパーを通じて、名前空間AppService
でのtime()
のすべての呼び出しをプロキシします。
テストで結果をモックしたい場合は、必要なテスト ケースに次のコードを記述する必要があります。
MockerState::addCondition( 'AppService', // 名前空間 'time', // 関数名 [], // 引数 100 // 結果);
コールバックを使用して関数の結果を設定することもできます。
MockerState::addCondition( '', // 名前空間 'headers_sent', // 関数名 [null, null], // 両方の引数は参照であり、関数呼び出しではまだ初期化されていません fn (&$file, &$line) => $file = $line = 123, // コールバック結果);
したがって、テストケースは次のようになります。
<?phpnamespace AppTests;use AppService;use PHPUnitFrameworkTestCase;class ServiceTest extends TestCase {パブリック関数 testRun2(): void{$service = new Service(); MockerState::addCondition('AppService','time', [],100);$this->assertEquals(100, $service->doSomething()); } }
XepozzInternalMockerTestsIntegrationDateTimeTest::testRun2
の完全な例を参照してください。
事前定義されたモックを使用すると、動作をグローバルにモックできます。
つまり、プロジェクト全体をモックしたい場合、各テスト ケースにMockerState::addCondition(...)
を記述する必要はありません。
異なる名前空間の同じ関数は、
Mocker
では同じではないことに注意してください。
そこで、作成したMockerExtension::executeBeforeFirstTest
に戻り、 $mocks
変数を編集します。
$モック = [ ['namespace' => 'AppService','name' => 'time','result' => 150,'arguments' => [], ]、 ];
このバリアントの後、各AppServicetime()
150
を返します。
たくさんのモックを追加できます。 Mocker
arguments
値を呼び出し関数の引数と比較し、必要な結果を返します。
混合とは、最初は事前定義されたモックを使用し、その後はランタイム モックを使用できることを意味します。
Runtime mock
使用する場合、関数をモックした後も別のテスト ケースでモックが残っているという問題に直面する可能性があります。
MockerState::saveState()
とMockerState::resetState()
この問題を解決します。
これらのメソッドは、「現在の」状態を保存し、適用された各Runtime mock
モックをアンロードします。
Mocker->load($mocks)
の後にMockerState::saveState()
を使用すると、事前定義されたモックのみが保存されます。
MockerState::getTraces()
メソッドを使用して、モック化された関数の呼び出しを追跡できます。
$traces = MockerState::getTraces('AppService', 'time');
$traces
次の構造の配列の配列が含まれます。
[ ['arguments' => [], // 関数の引数'trace' => [], // debug_backtrace の結果 function'result' => 1708764835, // 関数の結果],// ... 】
すべての内部関数は、元の関数と互換性があるようにスタブ化されています。これにより、関数は元の関数と同様に参照引数 ( &$file
) を使用するようになります。
これらはsrc/stubs.php
ファイルにあります。
新しい関数シグネチャを追加する必要がある場合は、 Mocker
コンストラクターの 2 番目の引数をオーバーライドします。
$mocker = new Mocker(stubPath: '/path/to/your/stubs.php');
グローバル関数をモックする方法はphp.ini
で無効にすることです: https://www.php.net/manual/en/ini.core.php#ini.disable-functions
最善の方法は、追加のフラグを指定してコマンドを実行し、テストの場合のみ無効にすることです。
php -ddisable_functions=${関数} ./vendor/bin/phpunit
PHPStorm を使用している場合は、
Run/Debug Configurations
セクションでコマンドを設定できます。Interpreter options
フィールドにフラグ-ddisable_functions=${functions}
を追加します。
このコマンドは、
composer.json
ファイルのscripts
セクションに保存しておくこともできます。
{ "スクリプト": {"テスト": "php -ddisable_functions=time,serialize,header,date ./vendor/bin/phpunit" } }
${functions}
カンマで区切ったモック対象の関数のリストに置き換えます (例:time,rand
。
これで、グローバル関数もモックできるようになりました。
php.ini
で関数を無効にすると、その関数を呼び出すことができなくなります。つまり、自分で実装する必要があります。
明らかに、PHP で実装されているほぼすべての関数は、Bash の関数と同じように見えます。
関数を実装する最も簡単な方法は、 `bash command`
構文を使用することです。
$mocks[] = [ 'namespace' => '', 'name' => 'time', 'function' => fn () => `date +%s`, ];
グローバル関数を実装せずに放置すると、関数のリコースコールが発生し、致命的なエラーが発生することに注意してください。
namespace
を強制的に使用しないとモックされた関数がモックされない場合、場合によっては不快な状況に直面することがあります。
function
。 @dataProvider
に PHP インタープリター ファイルを作成しようとしている可能性があります。これには注意してください。回避策として、テストのコンストラクターでモッカーを呼び出すことをお勧めします。したがって、まず拡張メソッドのexecuteBeforeFirstTest
からすべてのコードを新しい静的メソッドに移動し、それをexecuteBeforeFirstTest
と__construct
メソッドの両方で呼び出します。
最終クラス MyTest は PHPUnitFrameworkTestCase を拡張します {パブリック関数 __construct(?string $name = null, array $data = [], $dataName = '') {AppTestsMockerExtension::load();parent::__construct($name, $data, $dataName); } /// ...}
最終クラス MockerExtension は BeforeTestHook、BeforeFirstTestHook を実装します {パブリック関数executeBeforeFirstTest(): void{self::load(); }パブリック静的関数load(): void{$mocks = [];$mocker = new Mocker();$mocker->load($mocks); MockerState::saveState(); }パブリック関数executeBeforeTest(string $test): void{ MockerState::resetState(); } }
これはすべて、PHPUnit 9.5 以前のイベント管理システムが原因です。データ プロバイダーの機能はイベントの前に動作し始めるため、ランタイムの開始時に関数をモックすることは不可能です。