dobrosite/phpunit-psr-http-client

用于测试使用PSR-18的代码的工具

0.2.0 2022-08-14 17:27 UTC

This package is auto-updated.

Last update: 2024-09-14 22:05:58 UTC


README

这是PHPUnit的扩展,允许在集成和功能(应用)测试中为通过兼容PSR-18的HTTP客户端执行的请求设置预期。

示例

$this->getHttpClient()
    ->expectRequest('POST', 'https://example.com/api/v1/users')
    ->headers([
        'accept' => 'application/vnd.api+json',
        'content-type' => 'application/vnd.api+json',
    ])
    ->body([
        'data' => [
            'type' => 'users',
            'attributes' => [
                'email' => 'user@example.com',
            ],
        ],
    ])
    ->willReturn(
        [
            'errors' => [
                [
                    'code' => 'EmailAlreadyRegistered',
                    'title' => 'Адрес электронной почты уже заргистрирован',
                ],
            ],
        ],
        422,
        [
            'content-type' => 'application/vnd.api+json',
        ]
    );

安装

composer require dobrosite/phpunit-psr-http-client

连接

在您的应用程序的测试配置中,您需要用DobroSite\PHPUnit\PSR18\TestHttpClient的实例替换使用的Psr\Http\Client\ClientInterface实现。具体如何操作取决于您的应用程序架构,以下为一些流行框架的示例。

Symfony

向测试依赖项容器配置(通常是config/services_test.yaml)中添加以下内容

services:

  Psr\Http\Client\ClientInterface:
    class: DobroSite\PHPUnit\PSR18\TestHttpClient
    public: true

现在,在继承自Symfony\Bundle\FrameworkBundle\Test\KernelTestCase的测试中添加DobroSite\PHPUnit\PSR18\Symfony\TestHttpClientTrait混入

use DobroSite\PHPUnit\PSR18\Symfony\TestHttpClientTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

final class SomeTest extends WebTestCase
{
    use TestHttpClientTrait;

    public function testSomething(): void
    {
        // Подготовка.
        
        $this->getHttpClient()
            ->expectRequest('GET', 'https://some.service/some/resource')
            ->willReturn([
                'data' => [/* Имитация ответа сторонней службы. */],
            ]);
        
        // Действие.
        
        $client = static::createClient();
        $crawler = $client->request('GET', '/api/foo');

        // Проверки.

        $this->assertResponseIsSuccessful();
    }
}

工作原理

假设您有一个类Foo,它调用https://example.com/api的第三方API。为此类使用一个兼容PSR-18的HTTP客户端。

use Psr\Http\Client\ClientInterface;

class Foo 
{
    public function __construct(
        private readonly ClientInterface $httpClient,
    ) {
    }
    
    public function doSomething(): Something
    {
        // …
        $response = $this->httpClient->sendRequest($request);
        // …
    }
}

在测试中,将传递TestHttpClient实例给此类的构造函数,该实例将模拟与远程服务器的数据交换,而不执行实际请求。

使用方法

在执行检查动作之前,需要配置TestHttpClient——指定测试代码要执行哪些请求以及相应的响应。

expectRequest

对于每个预期的请求,需要调用接受两个参数的方法TestHttpClient::expectRequest()——$method和$uri,每个参数都可以是字符串或PHPUnit\Framework\Constraint\Constraint的实例。如果检查的代码执行了不匹配预期(或根本未执行)的HTTP请求,则测试将失败。

$this->getHttpClient()->expectRequest('GET', 'https://some.service/some/resource')

期望一个向https://some.service/some/resource发出GET请求。

$this->getHttpClient()->expectRequest(new IsAnything(), new StringStartsWith('https://example.com/'))
// или
$this->getHttpClient()->expectRequest(self::anything(), self::stringStartsWith('https://example.com/'))

期望一个以https://example.com/开头的请求。

headers

headers方法允许为测试代码要执行的请求的预期设置请求头。该方法仅接受一个参数,该参数应包含一个关联数组,其键是头名称(不区分大小写),值是预期值。值可以是字符串或Constraint的实例。

$this->getHttpClient()->expectRequest(/* … */)->headers([
    'Accept' => self::stringContains('application/json'),
    'content-type' => 'application/json',
]);

body

body方法设置请求体的预期。在方法的$bodyConstraint参数中,可以接受以下内容:

  • 字符串——将被解释为new IsEqual($bodyConstraint);
  • 数组——将被解释为new JsonMatches(json_encode($bodyConstraint));
  • Constraint的实例。
$this->getHttpClient()->expectRequest(/* … */)->body('{"foo":"bar"}');
$this->getHttpClient()->expectRequest(/* … */)->body(['foo' => 'bar']);
$this->getHttpClient()->expectRequest(/* … */)->body(self::stringContains('bar'));

willReturn

willReturn方法允许为预期的请求设置响应。该方法有三个参数。

  • $body——响应体。可以是
    • StreamInterface的实例——将被原样返回;
    • 字符串或资源——将被转换为StreamInterface
    • 数组——将被转换为JSON;
    • null——响应体将不存在。
  • $statusCode——HTTP状态码;
  • $headers——响应头。关联数组“头名称 => 值”。头名称不区分大小写。
$this->getHttpClient()->expectRequest(/* … */)->willReturn(
    body: '{"foo":"bar"}',
    statusCode: 200,
);
$this->getHttpClient()->expectRequest(/* … */)->willReturn(
    body: null,
    statusCode: 204,
    headers: ['ETag' => 'viChieyiupaidahng6eiv3bohRohcohb']
);

willThrowException

抛出传递给方法的事件。

$this->getHttpClient()->expectRequest(/* … */)->willThrowException(new SomeException(/* … */));