{"id":20705142,"url":"https://github.com/xepozz/internal-mocker","last_synced_at":"2025-04-23T01:31:38.574Z","repository":{"id":57673438,"uuid":"481842279","full_name":"xepozz/internal-mocker","owner":"xepozz","description":"A tool for mocking internal php functions and classes","archived":false,"fork":false,"pushed_at":"2025-01-24T10:01:03.000Z","size":139,"stargazers_count":31,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-29T20:51:10.245Z","etag":null,"topics":["codeception","mock","php","phpunit","testing"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xepozz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-15T05:01:54.000Z","updated_at":"2025-01-24T09:58:42.000Z","dependencies_parsed_at":"2024-03-04T08:48:57.904Z","dependency_job_id":null,"html_url":"https://github.com/xepozz/internal-mocker","commit_stats":{"total_commits":25,"total_committers":1,"mean_commits":25.0,"dds":0.0,"last_synced_commit":"41aea59a3633966c41ea1988c0e02335d8e97de6"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xepozz%2Finternal-mocker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xepozz%2Finternal-mocker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xepozz%2Finternal-mocker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xepozz%2Finternal-mocker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xepozz","download_url":"https://codeload.github.com/xepozz/internal-mocker/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250352331,"owners_count":21416471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codeception","mock","php","phpunit","testing"],"created_at":"2024-11-17T01:16:21.449Z","updated_at":"2025-04-23T01:31:38.555Z","avatar_url":"https://github.com/xepozz.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nThe package helps mock internal php functions as simple as possible. Use this package when you need mock such\nfunctions as: `time()`, `str_contains()`, `rand`, etc.\n\n[![Latest Stable Version](https://poser.pugx.org/xepozz/internal-mocker/v/stable.svg)](https://packagist.org/packages/xepozz/internal-mocker)\n[![Total Downloads](https://poser.pugx.org/xepozz/internal-mocker/downloads.svg)](https://packagist.org/packages/xepozz/internal-mocker)\n[![phpunit](https://github.com/xepozz/internal-mocker/workflows/PHPUnit/badge.svg)](https://github.com/xepozz/internal-mocker/actions)\n\n# Table of contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Register a PHPUnit Extension](#register-a-phpunit-extension)\n    - [PHPUnit 9](#phpunit-9)\n    - [PHPUnit 10 and higher](#phpunit-10-and-higher)\n  - [Register mocks](#register-mocks)\n    - [Runtime mocks](#runtime-mocks)\n    - [Pre-defined mock](#pre-defined-mock)\n    - [Mix of two previous ways](#mix-of-two-previous-ways)\n  - [State](#state)\n  - [Tracking calls](#tracking-calls)\n- [Global namespaced functions](#global-namespaced-functions)\n  - [Internal functions](#internal-functions)\n    - [Internal function implementation](#internal-function-implementation)\n- [Restrictions](#restrictions)\n  - [Data Providers](#data-providers)\n\n## Installation\n\n```bash\ncomposer require xepozz/internal-mocker --dev\n```\n\n## Usage\n\nThe main idea is pretty simple: register a Listener for PHPUnit and call the Mocker extension first.\n\n### Register a PHPUnit Extension\n\n#### PHPUnit 9\n\n1. Create new file `tests/MockerExtension.php`\n2. Paste the following code into the created file:\n    ```php\n    \u003c?php\n    declare(strict_types=1);\n    \n    namespace App\\Tests;\n    \n    use PHPUnit\\Runner\\BeforeTestHook;\n    use PHPUnit\\Runner\\BeforeFirstTestHook;\n    use Xepozz\\InternalMocker\\Mocker;\n    use Xepozz\\InternalMocker\\MockerState;\n    \n    final class MockerExtension implements BeforeTestHook, BeforeFirstTestHook\n    {\n        public function executeBeforeFirstTest(): void\n        {\n            $mocks = [];\n    \n            $mocker = new Mocker();\n            $mocker-\u003eload($mocks);\n            MockerState::saveState();\n        }\n   \n        public function executeBeforeTest(string $test): void\n        {\n            MockerState::resetState();\n        }\n    }\n    ```\n3. Register the hook as extension in `phpunit.xml.dist`\n    ```xml\n    \u003cextensions\u003e\n        \u003cextension class=\"App\\Tests\\MockerExtension\"/\u003e\n    \u003c/extensions\u003e\n    ```\n\n#### PHPUnit 10 and higher\n\n1. Create new file `tests/MockerExtension.php`\n2. Paste the following code into the created file:\n    ```php\n    \u003c?php\n    declare(strict_types=1);\n    \n    namespace App\\Tests;\n    \n    use PHPUnit\\Event\\Test\\PreparationStarted;\n    use PHPUnit\\Event\\Test\\PreparationStartedSubscriber;\n    use PHPUnit\\Event\\TestSuite\\Started;\n    use PHPUnit\\Event\\TestSuite\\StartedSubscriber;\n    use PHPUnit\\Runner\\Extension\\Extension;\n    use PHPUnit\\Runner\\Extension\\Facade;\n    use PHPUnit\\Runner\\Extension\\ParameterCollection;\n    use PHPUnit\\TextUI\\Configuration\\Configuration;\n    use Xepozz\\InternalMocker\\Mocker;\n    use Xepozz\\InternalMocker\\MockerState;\n    \n    final class MockerExtension implements Extension\n    {\n        public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void\n        {\n            $facade-\u003eregisterSubscribers(\n                new class () implements StartedSubscriber {\n                    public function notify(Started $event): void\n                    {\n                        MockerExtension::load();\n                    }\n                },\n                new class implements PreparationStartedSubscriber {\n                    public function notify(PreparationStarted $event): void\n                    {\n                        MockerState::resetState();\n                    }\n                },\n            );\n        }\n    \n        public static function load(): void\n        {\n            $mocks = [];\n    \n            $mocker = new Mocker();\n            $mocker-\u003eload($mocks);\n            MockerState::saveState();\n        }\n    }\n    ```\n3. Register the hook as extension in `phpunit.xml.dist`\n    ```xml\n    \u003cextensions\u003e\n        \u003cbootstrap class=\"App\\Tests\\MockerExtension\"/\u003e\n    \u003c/extensions\u003e\n    ```\n\nHere you have registered extension that will be called every time when you run `./vendor/bin/phpunit`.\n\nBy default, all functions will be generated and saved into `/vendor/bin/xepozz/internal-mocker/data/mocks.php` file.\n\nOverride the first argument of the `Mocker` constructor to change the path:\n\n```php\n$mocker = new Mocker('/path/to/your/mocks.php');\n```\n\n### Register mocks\n\nThe package supports a few ways to mock functions:\n\n1. Runtime mocks\n2. Pre-defined mocks\n3. Mix of two previous ways\n\n#### Runtime mocks\n\nIf you want to make your test case to be used with mocked function you should register it before.\n\nBack to the created `MockerExtension::executeBeforeFirstTest` and edit the `$mocks` variable.\n\n```php\n$mocks = [\n    [\n        'namespace' =\u003e 'App\\Service',\n        'name' =\u003e 'time',\n    ],\n];\n```\n\nThis mock will proxy every call of `time()` under the namespace `App\\Service` through a generated wrapper.\n\nWhen you want to mock result in tests you should write the following code into needed test case:\n\n```php\nMockerState::addCondition(\n   'App\\Service', // namespace\n   'time', // function name\n   [], // arguments\n   100 // result\n);\n```\n\nYou may also use a callback to set the result of the function:\n\n```php\nMockerState::addCondition(\n   '', // namespace\n   'headers_sent', // function name\n   [null, null], // both arguments are references and they are not initialized yet on the function call\n   fn (\u0026$file, \u0026$line) =\u003e $file = $line = 123, // callback result\n);\n```\n\nSo your test case will look like the following:\n\n```php\n\u003c?php\nnamespace App\\Tests;\n\nuse App\\Service;\nuse PHPUnit\\Framework\\TestCase;\n\nclass ServiceTest extends TestCase\n{\n    public function testRun2(): void\n    {\n        $service = new Service();\n\n        MockerState::addCondition(\n            'App\\Service',\n            'time',\n            [],\n            100\n        );\n\n        $this-\u003eassertEquals(100, $service-\u003edoSomething());\n   }\n}\n```\n\nSee full example\nin [`\\Xepozz\\InternalMocker\\Tests\\Integration\\DateTimeTest::testRun2`](tests/Integration/DateTimeTest.php)\n\n#### Pre-defined mock\n\nPre-defined mocks allow you to mock behaviour globally.\n\nIt means that you don't need to write `MockerState::addCondition(...)` into each test case if you want to mock it for\nwhole project.\n\n\u003e Keep in mind that the same functions from different namespaces are not the same for `Mocker`.\n\nSo back to the created `MockerExtension::executeBeforeFirstTest` and edit the `$mocks` variable.\n\n```php\n$mocks = [\n    [\n        'namespace' =\u003e 'App\\Service',\n        'name' =\u003e 'time',\n        'result' =\u003e 150,\n        'arguments' =\u003e [],\n    ],\n];\n```\n\nAfter this variant each `App\\Service\\time()` will return `150`.\n\nYou can add a lot of mocks. `Mocker` compares the `arguments` values with arguments of calling function and returns\nneeded result.\n\n#### Mix of two previous ways\n\nMix means that you can use **_Pre-defined mock_** at first and **_Runtime mock_** after.\n\n### State\n\nIf you use `Runtime mock` you may face the problem that after mocking function you still have it mocked in another test\ncases.\n\n`MockerState::saveState()` and `MockerState::resetState()` solves this problem.\n\nThese methods save \"current\" state and unload each `Runtime mock` mock that was applied.\n\nUsing `MockerState::saveState()` after `Mocker-\u003eload($mocks)` saves only **_Pre-defined_** mocks.\n\n### Tracking calls\n\nYou may track calls of mocked functions by using `MockerState::getTraces()` method.\n\n```php\n$traces = MockerState::getTraces('App\\Service', 'time');\n```\n\n`$traces` will contain an array of arrays with the following structure:\n\n```php\n[\n    [\n        'arguments' =\u003e [], // arguments of the function\n        'trace' =\u003e [], // the result of debug_backtrace function\n        'result' =\u003e 1708764835, // result of the function\n    ],\n    // ...\n]\n```\n\n### Function signature stubs\n\nAll internal functions are stubbed to be compatible with the original ones.\nIt makes the functions use referenced arguments (`\u0026$file`) as the originals do.\n\nThey are located in the [`src/stubs.php`](src/stubs.php) file.\n\nIf you need to add a new function signature, override the second argument of the `Mocker` constructor:\n\n```php\n$mocker = new Mocker(stubPath: '/path/to/your/stubs.php');\n```\n\n## Global namespaced functions\n\n### Internal functions\n\nThe way you can mock global functions is to disable them\nin `php.ini`: https://www.php.net/manual/en/ini.core.php#ini.disable-functions\n\nThe best way is to disable them only for tests by running a command with the additional flags:\n\n```bash\nphp -ddisable_functions=${functions} ./vendor/bin/phpunit\n```\n\n\u003e If you are using PHPStorm you may set the command in the `Run/Debug Configurations` section.\n\u003e Add the flag `-ddisable_functions=${functions}` to the `Interpreter options` field.\n\n\u003e You may keep the command in the `composer.json` file under the `scripts` section.\n\n```json\n{\n  \"scripts\": {\n    \"test\": \"php -ddisable_functions=time,serialize,header,date ./vendor/bin/phpunit\"\n  }\n}\n```\n\n\u003e Replace `${functions}` with the list of functions that you want to mock, separated by commas, e.g.: `time,rand`.\n\nSo now you can mock global functions as well.\n\n#### Internal function implementation\n\nWhen you disable a function in `php.ini` you cannot call it anymore. That means you must implement it by yourself.\n\nObviously, almost all functions are implemented in PHP looks the same as the Bash ones.\n\nThe shortest way to implement a function is to use ``` `bash command` ``` syntax:\n\n```php\n$mocks[] = [\n   'namespace' =\u003e '',\n   'name' =\u003e 'time',\n   'function' =\u003e fn () =\u003e `date +%s`,\n];\n```\n\n\u003e Keep in mind that leaving a global function without implementation will cause a recourse call of the function,\n\u003e that will lead to a fatal error.\n\n## Restrictions\n\n### Data Providers\n\nSometimes you may face unpleasant situation when mocked function is not mocking without forced using `namespace`\n+ `function`.\nIt may mean that you are trying make PHP interpreter file in `@dataProvider`.\nBe careful of it and as a workaround I may suggest you to call the mocker in test's constructor.\nSo first move all code from your extension method `executeBeforeFirstTest` to new static method\nand call it in both `executeBeforeFirstTest` and `__construct` methods.\n\n```php\nfinal class MyTest extends \\PHPUnit\\Framework\\TestCase\n{\n    public function __construct(?string $name = null, array $data = [], $dataName = '')\n    {\n        \\App\\Tests\\MockerExtension::load();\n        parent::__construct($name, $data, $dataName);\n    }\n    \n    /// ...\n}\n```\n\n```php\nfinal class MockerExtension implements BeforeTestHook, BeforeFirstTestHook\n{\n    public function executeBeforeFirstTest(): void\n    {\n        self::load();\n    }\n\n    public static function load(): void\n    {\n        $mocks = [];\n\n        $mocker = new Mocker();\n        $mocker-\u003eload($mocks);\n        MockerState::saveState();\n    }\n\n    public function executeBeforeTest(string $test): void\n    {\n        MockerState::resetState();\n    }\n}\n```\n\nThat all because of PHPUnit 9.5 and lower event management system.\nData Provider functionality starts to work before any events, so it's impossible to mock the function at the beginning\nof\nthe runtime.  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxepozz%2Finternal-mocker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxepozz%2Finternal-mocker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxepozz%2Finternal-mocker/lists"}