Pest tests can use the same validator pipeline as PHPUnit through a custom-expectation plugin that ships in this package. The library runtime stays Pest-free — install Pest as a dev dependency in your project to activate the expectations.
- Installation
- Wiring the trait into Pest tests
expect(...)->toMatchOpenApiResponseSchema()expect(...)->toMatchOpenApiRequestSchema()- Constraints (v1)
composer require --dev pestphp/pest:^3.0The plugin is auto-loaded via Composer's autoload.files. No further wiring needed at install time. Pest 4 (PHPUnit ^12) support is tracked separately.
Mix ValidatesOpenApiSchema into the Pest test suite via uses(...)->in(...) in tests/Pest.php (or whatever Pest configuration file your project uses). The trait must be on the test class — typically through a base TestCase that already extends Laravel's testing harness:
// tests/Pest.php
use Studio\OpenApiContractTesting\Laravel\ValidatesOpenApiSchema;
use Tests\TestCase;
uses(TestCase::class, ValidatesOpenApiSchema::class)->in('Feature');Once wired, auto_assert and auto_validate_request work exactly as with PHPUnit — every Laravel HTTP helper call validates against the configured spec.
Explicit response-side validation reads naturally inside Pest's expectation grammar:
it('lists pets with the documented shape', function () {
$response = $this->getJson('/api/v1/pets');
expect($response)->toMatchOpenApiResponseSchema();
});Optional named arguments cover the per-call overrides:
// Pin this single assertion to a different spec (overrides default_spec
// and any #[OpenApiSpec] on the test class for one call only).
expect($response)->toMatchOpenApiResponseSchema(spec: 'admin');
// Skip body validation for specific status codes (regex strings, anchored
// automatically). The standard `skip_response_codes` config still applies;
// these add to it for one call only.
expect($response)->toMatchOpenApiResponseSchema(skipResponseCodes: ['503']);
// Override method / path explicitly when the auto-resolved values from
// app('request') aren't what you want to validate against.
expect($response)->toMatchOpenApiResponseSchema(method: 'GET', path: '/api/v1/pets');The spec: override is single-shot: the next assertion in the same test method falls back to attribute / config resolution. Coverage recording, WeakMap dedup, and the #[SkipOpenApi] advisory warning all behave the same as the PHPUnit assertResponseMatchesOpenApiSchema() flow.
Request-side validation accepts the Symfony Request Laravel hands out via app('request'):
it('accepts a documented request body shape', function () {
$this->postJson('/api/v1/pets', ['name' => 'Buddy']);
expect(app('request'))->toMatchOpenApiRequestSchema();
});The same spec: / method: / path: keyword arguments are accepted. The request bridge always runs (it bypasses the auto_validate_request config gate and #[SkipOpenApi]) because the explicit expectation reads as the user's direct intent. The response side warns when #[SkipOpenApi] and an explicit assertResponseMatchesOpenApiSchema() collide; the request side has no auto-vs-explicit advisory pattern to mirror, so silence on expect($request)->toMatchOpenApiRequestSchema() is the deliberate behaviour.
- Laravel only. The expectations require the running Pest test class to use
ValidatesOpenApiSchema. Standalone (Symfony / framework-less) Pest support against PSR-7 messages is tracked as a follow-up to #109. - Pest 3. Pest 4 (PHPUnit ^12) is not yet in the CI matrix.
- Pest discovery contract. The plugin guards against a missing Pest install at autoload time, so it is safe to leave installed in projects that don't actually use Pest. If
pestphp/pestis absent, the bootstrap is a true no-op.