Skip to content

Provide a public API for custom assertions #275

Description

@viktorprogger

Context

I am implementing a reusable custom assertion for an application-level integration test. In this case the assertion validates an HTTP response against an OpenAPI schema, but the same need applies to any domain-specific assertion.

Testo does not currently seem to expose a public API for recording a custom assertion result while keeping it integrated with the current test state, assertion history, and normal failure reporting.

Current workaround

The assertion has to call the internal state collector directly:

use Testo\Assert\Internal\StaticState;

final class AssertOpenApiResponse
{
    public function assert(ResponseInterface $response, string $method, string $path): void
    {
        try {
            $this->responseValidator->validate(
                new OperationAddress($path, strtolower($method)),
                $response,
            );

            /** @psalm-suppress InternalMethod */
            StaticState::success(
                $response,
                "matches OpenAPI schema '$method $path'",
                'OpenAPI validation passed',
            );
        } catch (ValidationFailed $e) {
            /** @psalm-suppress InternalMethod */
            StaticState::fail($e);
        }
    }
}

This also requires suppressing InternalClass (the class itself is marked @internal) and InternalMethod in Psalm. The application code therefore depends on an implementation detail of the assert plugin and may break when the internal assertion machinery changes.

Example from a real project: AssertOpenApiResponse.php.

Proposal

Please provide a supported public extension point for custom assertions. It should allow userland code to:

  • record a successful custom assertion with an actual value, assertion description, and context/message;
  • record a failed custom assertion using an existing Throwable or a supported failure object;
  • preserve Testo's regular assertion history and output;
  • work without importing Testo\\Assert\\Internal\\StaticState or suppressing @internal diagnostics.

Possible API shapes could be a public Assert::custom(...)/failure API, or a public assertion context/recorder passed to a custom assertion. The exact API is open for discussion; the important part is that custom assertions should not need to reach into Internal\\StaticState.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions