該套件有助於盡可能簡單地模擬內部 php 函數。當您需要模擬諸如time()
、 str_contains()
、 rand
等函數時,請使用此套件。
安裝
用法
運行時模擬
預定義模擬
之前兩種方式的混合
PHP 單元 9
PHPUnit 10 及更高版本
註冊 PHPUnit 擴充
註冊模擬
狀態
追蹤通話
全域命名空間函數
內部功能實現
內部功能
限制
數據提供者
作曲家需要 xepozz/internal-mocker --dev
主要想法非常簡單:為 PHPUnit 註冊一個監聽器並先呼叫 Mocker 擴充功能。
建立新檔案tests/MockerExtension.php
將以下程式碼貼到已建立的文件中:
<?phpdeclare(strict_types=1);命名空間 AppTests;使用 PHPUnitRunnerBeforeTestHook;使用 PHPUnitRunnerBeforeFirstTestHook;使用 PHPUnitRunnerBeforeTestHook;使用 PHPUnitRunnerBeforeFirstTestHook;使用 XepozzInternalMockerMocker;使用 XepozzInternalMockerMockerStateb; {公用函數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);命名空間 AppTests;使用 PHPUnitEventTestPreparationStarted;使用 PHPUnitEventTestPreparationStartedSubscriber;使用 PHPUnitEventTestSuiteStarted;使用 PHPUnit ameterCollection;使用 PHPUnitTextUIConfigurationConfiguration;使用 XepozzInternalMockerMocker;使用 Xe pozzInternalMockerMockerState;最終類別MockerExtension實現擴展 {public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void{$facade->registerSubscribers(new class () 實作 StartedSubscriber {public function notification(Started $event): void{ MockerExtension::load(); } },新類別實作PreparationStartedSubscriber {public function notification(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
建構函數的第一個參數來更改路徑:
$mocker = new Mocker('/path/to/your/mocks.php');
該套件支援幾種模擬函數的方法:
運行時模擬
預定義的模擬
之前兩種方式的混合
如果您想讓您的測試案例與模擬函數一起使用,您應該之前註冊它。
返回建立的MockerExtension::executeBeforeFirstTest
並編輯$mocks
變數。
$mocks = [ ['命名空間'=>'AppService','名稱'=>'時間', ], ];
此模擬將透過產生的包裝器代理程式命名空間AppService
下的每次time()
呼叫。
當您想要在測試中模擬結果時,您應該將以下程式碼寫入所需的測試案例中:
MockerState::addCondition( 'AppService', // 命名空間 'time', // 函數名 [], // 參數 100 // 結果);
您也可以使用回呼來設定函數的結果:
MockerState::addCondition( '', // 命名空間 'headers_sent', // 函數名 [null, null], // 兩個參數都是引用,並且在函數呼叫時尚未初始化 fn (&$file, &$line) => $file = $line = 123, // 回呼結果);
所以你的測試案例將如下所示:
<?phpnamespace AppTests;使用 AppService;使用 PHPUnitFrameworkTestCase;類別 ServiceTest 擴充 TestCase {公用函數testRun2(): void{$service = new Service(); MockerState::addCondition('AppService','時間', [],100);$this->assertEquals(100, $service->doSomething()); } }
請參閱XepozzInternalMockerTestsIntegrationDateTimeTest::testRun2
中的完整範例
預先定義的模擬允許您全域模擬行為。
這表示如果您想模擬整個項目,則無需將MockerState::addCondition(...)
寫入每個測試案例。
請記住,來自不同命名空間的相同函數對於
Mocker
來說並不相同。
所以回到建立的MockerExtension::executeBeforeFirstTest
並編輯$mocks
變數。
$mocks = [ ['namespace' => 'AppService','name' => '時間','result' => 150,'arguments' => [], ], ];
在此變體之後,每個AppServicetime()
將傳回150
。
您可以新增很多模擬。 Mocker
將arguments
值與呼叫函數的參數進行比較並傳回所需的結果。
Mix 意味著您可以先使用預先定義的模擬,然後使用執行時間模擬。
如果您使用Runtime mock
您可能會面臨這樣的問題:在模擬函數之後,您仍然在另一個測試案例中模擬它。
MockerState::saveState()
和MockerState::resetState()
解決了這個問題。
這些方法保存“當前”狀態並卸載所應用的每個Runtime mock
。
在Mocker->load($mocks)
之後使用MockerState::saveState()
僅儲存預先定義的模擬。
您可以使用MockerState::getTraces()
方法追蹤模擬函數的呼叫。
$traces = MockerState::getTraces('AppService', '時間');
$traces
將包含具有以下結構的陣列陣列:
[ ['arguments' => [], // 函數的參數'trace' => [], // debug_backtrace 函數的結果'result' => 1708764835, // 函數的結果],// ... ]
所有內部函數都被存根以與原始函數相容。它使函數像原始函數一樣使用引用參數( &$file
)。
它們位於src/stubs.php
檔案中。
如果需要新增的函式簽名,請覆寫Mocker
建構函式的第二個參數:
$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
部分設定命令。將標誌-ddisable_functions=${functions}
新增至Interpreter options
欄位。
您可以將該命令保留在
scripts
部分下的composer.json
檔案中。
{“腳本”:{“測試”:“php -ddisable_functions =時間,序列化,標題,日期./vendor/bin/phpunit” } }
將
${functions}
替換為要模擬的函數列表,以逗號分隔,例如:time,rand
。
所以現在您也可以模擬全域函數。
當您停用php.ini
中的函數時,您將無法再呼叫它。這意味著您必須自己實施它。
顯然,幾乎所有的功能都是用 PHP 實現的,看起來和 Bash 一樣。
實現功能的最短方法是使用`bash command`
語法:
$mocks[] = [ '命名空間' => '', '名稱' => '時間', '函數' => fn () => `日期 +%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 及更低版本的事件管理系統。資料提供者功能在任何事件之前開始工作,因此不可能在運行時開始模擬函數。