{"id":15014515,"url":"https://github.com/macpaw/extended_mock_http_client","last_synced_at":"2025-04-12T08:08:51.479Z","repository":{"id":38173143,"uuid":"273867626","full_name":"MacPaw/extended_mock_http_client","owner":"MacPaw","description":"ExtendedMockHttpClient for Symfony HTTP Client","archived":false,"fork":false,"pushed_at":"2024-01-15T15:53:41.000Z","size":349,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-12T08:07:13.000Z","etag":null,"topics":["fixtures","http-client","mock","phpunit","symfony","testing"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MacPaw.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-06-21T08:50:54.000Z","updated_at":"2023-04-13T02:21:45.000Z","dependencies_parsed_at":"2024-01-14T13:54:48.653Z","dependency_job_id":"94d4bfec-f711-4ddc-b8f4-107f2d26ca1b","html_url":"https://github.com/MacPaw/extended_mock_http_client","commit_stats":{"total_commits":96,"total_committers":7,"mean_commits":"13.714285714285714","dds":0.5104166666666667,"last_synced_commit":"c76ec5a932bc10d89fb0db2d951a06dd1b843af9"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Fextended_mock_http_client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Fextended_mock_http_client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Fextended_mock_http_client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Fextended_mock_http_client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MacPaw","download_url":"https://codeload.github.com/MacPaw/extended_mock_http_client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248537137,"owners_count":21120709,"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":["fixtures","http-client","mock","phpunit","symfony","testing"],"created_at":"2024-09-24T19:45:43.446Z","updated_at":"2025-04-12T08:08:51.445Z","avatar_url":"https://github.com/MacPaw.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ExtendedMockHttpClient\n\n| Version | Build Status | Code Coverage |\n|:---------:|:-------------:|:-----:|\n| `master`| [![CI][master Build Status Image]][master Build Status] | [![Coverage Status][master Code Coverage Image]][master Code Coverage] |\n\n## Install\n```shell script\ncomposer require macpaw/extended_mock_http_client\n```\n\n## How to use\n\nIn config file `config/services_test.yaml` replace current HTTP client service\n```yaml\nimports:\n    - { resource: services.yaml }\n\nservices:\n    _defaults:\n        autowire: true\n        autoconfigure: true\n        public: true\n    http_client_service_name:\n        class: ExtendedMockHttpClient\\ExtendedMockHttpClient\n        arguments:\n            - 'https://test.host'\n```\n\nThat's all, you can use it in PHPUnit tests\n\n## Examples\n\n#### Simple examples\n\n```php\nabstract class AbstractFunctionalTest extends KernelTestCase\n{\n    private ExtendedMockHttpClient $httpClient\n\n    protected function setUp(): void\n    {\n        /** @var ExtendedMockHttpClient $mockHttpClient */\n        $this-\u003ehttpClient = self::getContainer()-\u003eget('http_client_service_name');\n    }\n}\n\nclass MyTest extends AbstractFunctionalTest\n{\n    /**\n     * Create simple request using createFixture\n     * Request with almost empty parameters\n     * Check response and check called times\n     */\n    public function testSimpleExample1(): void\n    {\n        $httpFixture = $this-\u003eclient-\u003ecreateFixture(\n            'POST',\n            'https://test.test/foo?foo=bar',\n            null,\n            null,\n            200,\n            'ok'\n        );\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('POST', 'https://test.test/foo?foo=bar');\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n        self::assertEquals('ok', $response-\u003egetContent());\n        self::assertEquals(1, $httpFixture-\u003egetCalledTimes());\n    }\n\n    /**\n     * Make simple fixture using createFixture\n     * Request using json\n     * Check response\n     */\n    public function testSimpleExample2(): void\n    {\n        $httpFixture = $this-\u003eclient-\u003ecreateFixture(\n            'POST',\n            'https://test.test/foo?foo=bar',\n            '{\"foo\":\"bar\",\"baz\":123}',\n            [\n                'x-header' =\u003e 'x-value',\n            ],\n            200,\n            'ok'\n        );\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('POST', 'https://test.test/foo?foo=bar', [\n            'json' =\u003e [\n                'foo' =\u003e 'bar',\n                'baz' =\u003e 123\n            ],\n            'headers' =\u003e [\n                'x-header' =\u003e 'x-value',\n            ]\n        ]);\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n        self::assertEquals('ok', $response-\u003egetContent());\n    }\n}\n```\n\n#### Using builder examples\n\n```php\nclass MyTest extends AbstractFunctionalTest\n{\n    /**\n     * Make fixture using builder\n     * Request using json\n     * Check response\n     */\n    public function testBuilderExample1(): void\n    {\n        $builder = $this-\u003eclient-\u003egetHttpFixtureBuilder();\n\n        $httpFixture = $builder\n            -\u003erequest(\n                $builder-\u003emethod(['PUT', 'POST']),\n                $builder-\u003eurl('https://test.test/foo'),\n                $builder-\u003equery([\n                    'foo' =\u003e 'bar',\n                ]),\n                $builder-\u003ebody($builder-\u003ejsonToArray(\n                    $builder-\u003earrayContain([\n                        'foo' =\u003e 'bar',\n                    ])\n                )),\n                $builder-\u003eheaders([\n                    'x-header' =\u003e 'x-value',\n                ])\n            )\n            -\u003eresponse(200, 'ok')\n            -\u003ebuild();\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('POST', 'https://test.test/foo?foo=bar', [\n            'json' =\u003e [\n                'foo' =\u003e 'bar',\n                'baz' =\u003e 123\n            ],\n            'headers' =\u003e [\n                'x-header' =\u003e 'x-value',\n            ]\n        ]);\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n        self::assertEquals('ok', $response-\u003egetContent());\n    }\n\n    /**\n     * Make fixture using builder with MockResponse\n     * Request using json\n     * Check response\n     */\n    public function testBuilderExample2(): void\n    {\n        $builder = $this-\u003eclient-\u003egetHttpFixtureBuilder();\n\n        $httpFixture = $builder\n            -\u003erequest(\n                $builder-\u003emethod('POST'),\n                $builder-\u003eurl('https://test.test/foo'),\n                $builder-\u003equery($builder-\u003equeryToArray($builder-\u003earrayContain([\n                    'foo' =\u003e 'bar',\n                ]))),\n                $builder-\u003ebody($builder-\u003estringRegex('/\"foo\":\"bar\"/')),\n                $builder-\u003eheaders([\n                    'x-header' =\u003e 'x-value',\n                ])\n            )\n            -\u003eresponse(new MockResponse('ok', ['http_code' =\u003e 200]))\n            -\u003ebuild();\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('POST', 'https://test.test/foo?foo=bar', [\n            'json' =\u003e [\n                'foo' =\u003e 'bar',\n                'baz' =\u003e 123\n            ],\n            'headers' =\u003e [\n                'x-header' =\u003e 'x-value',\n            ]\n        ]);\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n        self::assertEquals('ok', $response-\u003egetContent());\n    }\n}\n```\n\n#### Using callbacks in request and response examples\n\n```php\nclass MyTest extends AbstractFunctionalTest\n{\n    /**\n     * Make fixture using builder with callbacks in request and response\n     * Request using json\n     * Check response\n     */\n    public function testCallbackExample(): void\n    {\n        $builder = $this-\u003eclient-\u003egetHttpFixtureBuilder();\n\n        $httpFixture = $builder\n            -\u003erequest(\n                $builder-\u003emethod($builder-\u003ecallback(function (string $method): bool {\n                    return $method === 'POST';\n                })),\n                $builder-\u003eurl($builder-\u003ecallback(function (string $url): bool {\n                    return $url === 'https://test.test/foo';\n                })),\n                $builder-\u003equery(\n                    $builder-\u003ecallback(function (string $query): bool {\n                        return $query === 'foo=bar';\n                    }),\n                    $builder-\u003equeryToArray(\n                        $builder-\u003ecallback(function (array $arrayQuery): bool {\n                            return array_key_exists('foo', $arrayQuery);\n                        })\n                    )\n                ),\n                $builder-\u003ebody($builder-\u003ecallback(function (string $jsonBody): bool {\n                    $arrayBody = json_decode($jsonBody, true);\n\n                    return isset($arrayBody['foo']);\n                })),\n                $builder-\u003eheaders($builder-\u003ecallback(function (array $headers): bool {\n                    return array_key_exists('x-header', $headers);\n                }))\n            )\n            -\u003eresponse(\n                function (string $method, string $url, string $query, string $body, array $headers): MockResponse {\n                    $stringHeaders = [];\n                    foreach ($headers as $key =\u003e $value) {\n                        $stringHeaders[] = \"$key: $value\";\n                    }\n\n                    return new MockResponse(json_encode([\n                        'method' =\u003e $method,\n                        'url' =\u003e $url,\n                        'query' =\u003e $query,\n                        'body' =\u003e $body,\n                        'headers' =\u003e $headers,\n                    ]));\n                }\n            )\n            -\u003ebuild();\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('POST', 'https://test.test/foo?foo=bar', [\n            'json' =\u003e [\n                'foo' =\u003e 'bar',\n                'baz' =\u003e 123\n            ],\n            'headers' =\u003e [\n                'x-header' =\u003e 'x-value',\n            ]\n        ]);\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n\n        $responseArray = json_decode($response-\u003egetContent(), true);\n        self::assertEquals('POST', $responseArray['method']);\n        self::assertEquals('https://test.test/foo', $responseArray['url']);\n        self::assertEquals('foo=bar', $responseArray['query']);\n        self::assertEquals('{\"foo\":\"bar\",\"baz\":123}', $responseArray['body']);\n        self::assertArrayHasKey('x-header', $responseArray['headers']);\n    }    \n}\n```\n\n#### Hot to register custom Comparator\n\nCreate comparator class, it should implement `ComparatorInterface`\n\n```php\nuse ExtendedMockHttpClient\\HttpFixture\\Request\\Comparator\\ComparatorInterface;\n\nclass CustomComparator implements ComparatorInterface\n{\n    /**\n     * @var string\n     */\n    private $stringPart1;\n\n    /**\n     * @var string\n     */\n    private $stringPart2;\n\n    public static function getName(): string\n    {\n        return 'custom';\n    }\n\n    public function __construct(string $stringPart1, string $stringPart2)\n    {\n        $this-\u003estringPart1 = $stringPart1;\n        $this-\u003estringPart2 = $stringPart2;\n    }\n\n    public function __invoke($value): bool\n    {\n        return $value === \"$this-\u003estringPart1.$this-\u003estringPart2\";\n    }\n}\n```\n\nOverwrite `HttpFixtureFactory` for adding where you can use the new comparator\n\n```yaml\nservices:\n    ExtendedMockHttpClient\\Factory\\HttpFixtureFactory:\n        arguments:\n            - '%allowed_nested_keys%'\n        calls:\n            - add: ['body', 'custom']\n            - add: ['method', 'custom']\n            - add: ['query', 'custom']\n            ...\n```\n\nUse the new comparator in test\n\n```php\nclass MyTest extends AbstractFunctionalTest\n{\n    /**\n     * Make fixture using builder with custom comparator\n     * Request using string body\n     * Check response\n     */\n    public function testCustomComparator(): void\n    {\n        $builder = $this-\u003eclient-\u003egetHttpFixtureBuilder();\n\n        $httpFixture = $builder\n            -\u003erequest(\n                $builder-\u003ebody(new CustomComparator('foo', 'bar'))\n            )\n            -\u003eresponse(200, 'ok')\n            -\u003ebuild();\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('GET', 'https://test.test', [\n            'body' =\u003e 'foo.bar'\n        ]);\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n        self::assertEquals('ok', $response-\u003egetContent());\n    }\n}\n```\n\n#### Hot to overwrite HttpFixtureBuilderFactory for using more useful builder method\n\nCreate custom builder class which based on original builder\n\n```php\nuse ExtendedMockHttpClient\\Builder\\HttpFixtureBuilder as BaseHttpFixtureBuilder;\nuse ExtendedMockHttpClient\\Tests\\Fixture\\Application\\HttpFixture\\Request\\Comparator\\CustomComparator;\n\nclass HttpFixtureBuilder extends BaseHttpFixtureBuilder\n{\n    public function custom(string $stringPart1, string $stringPart2): CustomComparator\n    {\n        return new CustomComparator($stringPart1, $stringPart2);\n    }\n}\n```\n\nCreate custom builder factory class which based on original builder factory\n\n```php\nuse ExtendedMockHttpClient\\Factory\\HttpFixtureBuilderFactory as BaseHttpFixtureBuilderFactory;\nuse ExtendedMockHttpClient\\Builder\\HttpFixtureBuilder as BaseHttpFixtureBuilder;\nuse ExtendedMockHttpClient\\Tests\\Fixture\\Application\\Builder\\HttpFixtureBuilder;\n\nclass HttpFixtureBuilderFactory extends BaseHttpFixtureBuilderFactory\n{\n    public function create(): BaseHttpFixtureBuilder\n    {\n        return new HttpFixtureBuilder($this-\u003ehttpFixtureFactory);\n    }\n}\n```\n\nOverwrite builder factory service\n```yaml\nservices:\n    ExtendedMockHttpClient\\Factory\\HttpFixtureBuilderFactory:\n        class: ExtendedMockHttpClient\\Tests\\Fixture\\Application\\Factory\\HttpFixtureBuilderFactory\n```\n\nUse updated builder in tests\n\n```php\nclass MyTest extends AbstractFunctionalTest\n{\n    /**\n     * Make fixture using overwrote builder with custom comparator\n     * Request using string body\n     * Check response\n     */\n    public function testBuilderOverwrote(): void\n    {\n        /** @var HttpFixtureBuilder $builder */\n        $builder = $this-\u003eclient-\u003egetHttpFixtureBuilder();\n\n        $httpFixture = $builder\n            -\u003erequest(\n                $builder-\u003ebody($builder-\u003ecustom('foo', 'bar'))\n            )\n            -\u003eresponse(200, 'ok')\n            -\u003ebuild();\n        $this-\u003eclient-\u003eaddFixture($httpFixture);\n\n        $response = $this-\u003eclient-\u003erequest('GET', 'https://test.test', [\n            'body' =\u003e 'foo.bar'\n        ]);\n\n        self::assertEquals(200, $response-\u003egetStatusCode());\n        self::assertEquals('ok', $response-\u003egetContent());\n    }\n}\n```\n\n## Todo list\n* Add support jms serializer \n* Add history function\n  * Get last request/response (or by index)\n  * Some kind of assert, it should check that history contain some request\n* Add possibility to load fixtures from array/yaml\n* Add logger and log every steps for easiest debug\n\n[master Build Status]: https://github.com/macpaw/ExtendedMockHttpClient/actions?query=workflow%3ACI+branch%3Amaster\n[master Build Status Image]: https://github.com/macpaw/ExtendedMockHttpClient/workflows/CI/badge.svg?branch=master\n[master Code Coverage]: https://codecov.io/gh/macpaw/ExtendedMockHttpClient/branch/master\n[master Code Coverage Image]: https://img.shields.io/codecov/c/github/macpaw/ExtendedMockHttpClient/master?logo=codecov\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmacpaw%2Fextended_mock_http_client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmacpaw%2Fextended_mock_http_client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmacpaw%2Fextended_mock_http_client/lists"}