meritum/http-testing

HTTP request/response testing helpers for the Meritum ecosystem

Maintainers

Package info

github.com/MeritumIO/http-testing

pkg:composer/meritum/http-testing

Transparency log

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-07-16 16:07 UTC

This package is auto-updated.

Last update: 2026-07-16 16:10:57 UTC


README

HTTP request/response testing helpers for the Meritum ecosystem — builds a PSR-7 request, drives it through any PSR-15 RequestHandlerInterface, and wraps the resulting response for assertions. No dependency on meritum/testing or meritum/http — it works against the standard PSR-7/PSR-15 interfaces, so any consumer's app kernel qualifies as long as it implements RequestHandlerInterface. Retrieving that handler from wherever a project stores it (a test kernel, a bare property, etc.) is the consuming project's own responsibility, not this package's.

Requirements

  • PHP 8.4+
  • phpunit/phpunit ^11.0
  • psr/http-message ^1.1
  • psr/http-server-handler ^1.0
  • laminas/laminas-diactoros ^3.0

Installation

composer require meritum/http-testing

Usage

HttpTestingTrait

The usual way to use this package: add the trait to a PHPUnit test case and implement getRequestHandler() to return whatever RequestHandlerInterface your app kernel exposes — however you obtain it is up to you:

use PHPUnit\Framework\TestCase;
use Psr\Http\Server\RequestHandlerInterface;
use Meritum\HttpTesting\HttpTestingTrait;

abstract class ApiTestCase extends TestCase
{
    use HttpTestingTrait;

    protected function getRequestHandler(): RequestHandlerInterface
    {
        return $this->kernel->get(HttpKernelInterface::class); // however your project stores it
    }
}

Every test then gets get(), post(), put(), patch(), delete(), head(), and options(), each returning a TestResponse:

final class PostsTest extends ApiTestCase
{
    public function test_it_creates_a_post(): void
    {
        $this->post('/posts', ['title' => 'Hello'])
             ->assertCreated()
             ->assertAttributeEquals('data.title', 'Hello');
    }
}

getRequestHandler() is only called once per test — the trait caches the Client it builds around the returned handler, so it's safe (and expected) to resolve the handler from a kernel or container each time without worrying about rebuilding it per request.

Client

The lower-level piece the trait is built on, for anyone not using PHPUnit test cases directly:

use Meritum\HttpTesting\Client;

$client = new Client($handler); // any Psr\Http\Server\RequestHandlerInterface

$response = $client->request('POST', '/posts', ['title' => 'Hello']);

For POST/PUT/PATCH, the parsed body is JSON-encoded and written directly into the request's body stream (not set as PSR-7's parsedBody) — this matches how most middleware pipelines actually read the request body, rather than relying on parsedBody being populated. Content-Type: application/json is set by default for those methods unless a header already provides one.

TestResponse

Wraps whatever ResponseInterface comes back, with chainable assertions:

  • assertStatus(int $status) plus shortcuts for the common codes — assertOk, assertCreated, assertAccepted, assertNoContent, assertMovedPermanently, assertFound, assertBadRequest, assertUnauthorized, assertForbidden, assertNotFound, assertMethodNotAllowed, assertNotAcceptable, assertRequestTimeout, assertConflict, assertGone, assertUnsupportedMediaType, assertUnprocessableContent, assertTooManyRequests, assertInternalServerError, assertBadGateway, assertServiceUnavailable, assertGatewayTimeout
  • assertHeader(string $name, string $value)
  • assertBodyEquals(array $expected) — loose equality against the full decoded JSON body
  • assertAttributeExists(string $path) / assertAttributeType(string $path, string $type) / assertAttributeEquals(string $path, mixed $value) — dot-path lookups resolved from the response body root (e.g. 'data.name', not assumed to start under any particular key), so these work equally well against a success envelope or an error envelope
  • getResponse(): ResponseInterface / getResponseBody(): array — escape hatches for anything not covered by the assertion methods above

Every assertion method returns $this, so they chain:

$this->get('/posts/1')
     ->assertOk()
     ->assertHeader('Content-Type', 'application/json')
     ->assertAttributeEquals('data.id', '1');