定制 holgerk/guzzle-replay 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

holgerk/guzzle-replay

Composer 安装命令:

composer require holgerk/guzzle-replay

包简介

A replay middleware for easier testing guzzle clients

README 文档

README

GitHub Release GitHub Actions Workflow Status Packagist Downloads

Record guzzle requests and have them replayed during next runs. This helps creating tests for http api clients.

Install

composer require holgerk/guzzle-replay --dev

Usage

use GuzzleHttp\Client;
use Holgerk\GuzzleReplay\Mode;
use Holgerk\GuzzleReplay\GuzzleReplay;

$guzzleClient = new Client();
// create middleware either in recording or in replay mode
//$middleware = GuzzleReplay::create(Mode::Replay);
$middleware = GuzzleReplay::create(Mode::Record);
// inject middleware into guzzle client
$middleware->inject($guzzleClient);
// inject guzzle client into to your api client that you want to test
$apiClient = new GithubApiClient($guzzleClient);
// do your tests with the api client...

Example

SimpleApiClient.php
use GuzzleHttp\Client;

class SimpleApiClient
{
    public function __construct(private Client $client) {}

    public function getUuid(): string
    {
        $content = $this->client
            ->get('https://httpbin.org/uuid')
            ->getBody()
            ->getContents();
        return json_decode($content, true)['uuid'];
    }
}
SimpleApiClientTest.php
use GuzzleHttp\Client;
use Holgerk\GuzzleReplay\GuzzleReplay;
use PHPUnit\Framework\TestCase;
use function PHPUnit\Framework\assertEquals;

class SimpleApiClientTest extends TestCase
{
    public function testGetUuid(): void
    {
        // GIVEN
        $guzzleClient = new Client();
        //$middleware = GuzzleReplay::create(GuzzleReplay::MODE_RECORD);
        $middleware = GuzzleReplay::create(GuzzleReplay::MODE_REPLAY);
        $middleware->inject($guzzleClient);

        // WHEN
        $apiClient = new SimpleApiClient($guzzleClient);
        $firstUuid = $apiClient->getUuid();
        $secondUuid = $apiClient->getUuid();

        // THEN
        assertEquals('bdd37445-2455-44d3-a00d-18d6220ff565', $firstUuid);
        assertEquals('ea175c37-df2a-42aa-b9b8-c3eac8dfb80b', $secondUuid);
    }
}

The following file is generated on first recording and updated on following recordings. (for this you need to create the middleware with record mode: GuzzleReplay::create(GuzzleReplay::MODE_RECORD)) It contains all responses and requests that happen during the recording. The name of the file is composed of test class name, test method name and the suffix: "_guzzleRecording.php". If you don't like to have the recordings in separate files you can opt-out (see: Recording to method and not to a file)

SimpleApiClientTest_testGetUuid_guzzleRecording.php
<?php

// GENERATED - DO NOT EDIT
return \Holgerk\GuzzleReplay\Recording::fromArray(
    [
        'records' => [
            [
                'requestModel' => [
                    'method' => 'GET',
                    'uri' => 'https://httpbin.org/uuid',
                    'headers' => [
                        'User-Agent' => [
                            'GuzzleHttp/7',
                        ],
                        'Host' => [
                            'httpbin.org',
                        ],
                    ],
                    'body' => '',
                    'version' => '1.1',
                ],
                'responseModel' => [
                    'status' => 200,
                    'headers' => [
                        'Date' => [
                            'Wed, 07 May 2025 09:16:13 GMT',
                        ],
                        'Content-Type' => [
                            'application/json',
                        ],
                        'Content-Length' => [
                            '53',
                        ],
                        'Connection' => [
                            'keep-alive',
                        ],
                        'Server' => [
                            'gunicorn/19.9.0',
                        ],
                        'Access-Control-Allow-Origin' => [
                            '*',
                        ],
                        'Access-Control-Allow-Credentials' => [
                            'true',
                        ],
                    ],
                    'body' => '{'."\n"
                        .'  "uuid": "bdd37445-2455-44d3-a00d-18d6220ff565"'."\n"
                        .'}'."\n",
                    'version' => '1.1',
                    'reason' => 'OK',
                    'decodedBody' => [
                        'uuid' => 'bdd37445-2455-44d3-a00d-18d6220ff565',
                    ],
                ],
            ],
            [
                'requestModel' => [
                    'method' => 'GET',
                    'uri' => 'https://httpbin.org/uuid',
                    'headers' => [
                        'User-Agent' => [
                            'GuzzleHttp/7',
                        ],
                        'Host' => [
                            'httpbin.org',
                        ],
                    ],
                    'body' => '',
                    'version' => '1.1',
                ],
                'responseModel' => [
                    'status' => 200,
                    'headers' => [
                        'Date' => [
                            'Wed, 07 May 2025 09:16:13 GMT',
                        ],
                        'Content-Type' => [
                            'application/json',
                        ],
                        'Content-Length' => [
                            '53',
                        ],
                        'Connection' => [
                            'keep-alive',
                        ],
                        'Server' => [
                            'gunicorn/19.9.0',
                        ],
                        'Access-Control-Allow-Origin' => [
                            '*',
                        ],
                        'Access-Control-Allow-Credentials' => [
                            'true',
                        ],
                    ],
                    'body' => '{'."\n"
                        .'  "uuid": "ea175c37-df2a-42aa-b9b8-c3eac8dfb80b"'."\n"
                        .'}'."\n",
                    'version' => '1.1',
                    'reason' => 'OK',
                    'decodedBody' => [
                        'uuid' => 'ea175c37-df2a-42aa-b9b8-c3eac8dfb80b',
                    ],
                ],
            ],
        ],
    ]
);

Errors for unexpected requests

Output
1) Holgerk\GuzzleReplay\Tests\examples\SimpleApiClientTest::testMultipleRequests
Holgerk\GuzzleReplay\NoReplayFoundAssertionError:
| No replay found for this request:
| ---------------------------------
| - Request
|     method: GET
|     uri: https://httpbin.org/status/201
|     headers: {"User-Agent":["GuzzleHttp\/7"],"Host":["httpbin.org"]}
|     body:
|     version: 1.1
|
| Diff to best matching expected request:
| ---------------------------------------
| --- Expected
| +++ Actual
| @@ @@
|  Request
|      method: GET
| -    uri: https://httpbin.org/status/200
| +    uri: https://httpbin.org/status/201
|      headers: {"User-Agent":["GuzzleHttp\/7"],"Host":["httpbin.org"]}
|      body:
|      version: 1.1
|
| All expected requests (sorted by difference):
| ---------------------------------------------
| - Request
|     method: GET
|     uri: https://httpbin.org/status/200
|     headers: {"User-Agent":["GuzzleHttp\/7"],"Host":["httpbin.org"]}
|     body:
|     version: 1.1
|
| - Request
|     method: GET
|     uri: https://httpbin.org/status/303
|     headers: {"User-Agent":["GuzzleHttp\/7"],"Host":["httpbin.org"]}
|     body:
|     version: 1.1
|

Usage with Laravel Http Facade

use Illuminate\Support\Facades\Http;
use Holgerk\GuzzleReplay\GuzzleReplay;
use Holgerk\GuzzleReplay\Mode;
Http::globalMiddleware(GuzzleReplay::create(Mode::Replay));

Recording to method and not to a file

globally (for all tests)

use Holgerk\GuzzleReplay\MethodRecorder;
use Holgerk\GuzzleReplay\Options;
Options::$globalRecorderFactory = fn() => new MethodRecorder();

locally (within one test)

use Holgerk\GuzzleReplay\GuzzleReplay;
use Holgerk\GuzzleReplay\MethodRecorder;
use Holgerk\GuzzleReplay\Options;
$middleware = GuzzleReplay::create(
    Mode::Record, 
    Options::create()->setRecorder(new MethodRecorder())
);

Masking sensistive data

$middleware = GuzzleReplay::create(Mode::Replay, Options::create()
    ->setRequestTransformer(static function (RequestModel $requestModel) {
        // mask authorization token, to not leak sensitive data
        $requestModel->replaceString($_ENV['GITHUB_TOKEN'], 'XXX');
        // or you can unset the header 
        //unset($requestModel->headers['Authorization']);
        //$requestModel->removeHeader('content-length');
    })
);

Usage with dataProviders

public static function dataProviderTestGetStatusCode(): array
{
    return [
        'data-set-1' => ['givenStatusCode' => 201],
        'data-set-2' => ['givenStatusCode' => 400],
    ];
}

/**
 * @dataProvider dataProviderTestGetStatusCode
 */
public function testGetStatusCode(int $givenStatusCode): void
{
    // GIVEN
     
    // append status code to testMethodName so we get distinct 
    // recordings foreach data-set.
    $options = Options::create();
    $options->recordName->testMethodName .= $givenStatusCode;
    
    $guzzleClient = new Client();
    $middleware = GuzzleReplay::create(GuzzleReplay::MODE_REPLAY, $options);
    $middleware->inject($guzzleClient);

    // WHEN
    $apiClient = new SimpleApiClient($guzzleClient);
    $responseStatusCode = $apiClient->getStatusCode($givenStatusCode);

    // THEN
    assertEquals($givenStatusCode, $responseStatusCode);
}

Best practices

  • Use recordings to test every public method of your api client, buttests of your api client consumer you should use a mocked version of your api client. Otherwise you pollute your tests with redundant recordings, which require you todo many rerecordings, when you change the implementation of one of your api clients methods.

FAQ

  • Why not just mock the guzzle client? Yes, this it is also an option. But if you want to ensure that your api client does not haved changed the exact http invocation, which in fact invalidates your fixtured response, one would need alot of verification boilerplate. Also it is teddious to fixture the exact response.

License

The MIT License (MIT). Please see License File for more information.

holgerk/guzzle-replay 适用场景与选型建议

holgerk/guzzle-replay 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 5.27k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 05 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 holgerk/guzzle-replay 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 holgerk/guzzle-replay 我们能提供哪些服务?
定制开发 / 二次开发

基于 holgerk/guzzle-replay 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 5.27k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-05-06