hotmeteor/spectator
Spectator brings lightweight OpenAPI contract testing to your Laravel test suite. Write tests that guarantee your API spec never drifts from your implementation, with artisan validation commands, operation coverage tracking, and JSON error output for CI.
stdClass for empty x-www-form-urlencoded request bodies (fixes 500 on empty form posts against object schemas) by @egretos in #223spectator:routes output by @jaap in #222Full Changelog: https://github.com/hotmeteor/spectator/compare/v3.0.3...v3.0.4
Full Changelog: https://github.com/hotmeteor/spectator/compare/v3.0.1...v3.0.2
Full Changelog: https://github.com/hotmeteor/spectator/compare/v3.0.0...v3.0.1
v3 is a significant release that raises the platform floor, adds a suite of developer-experience tools, and fixes several validation edge cases.
Minimum requirements
New config key
A new error_format key is required in config/spectator.php. If you published the config in v2, add:
'error_format' => env('SPECTATOR_ERROR_FORMAT', 'text'),
No behaviour change unless you set SPECTATOR_ERROR_FORMAT=json.
Internal type changes (extenders only)
If you extend Spectator's validator classes, note that:
'read' / 'write' strings in AbstractValidator are now ValidationMode::Read / ValidationMode::Write enum casesFormat::TEXT_GREEN style class constants are now Format::TextGreen string-backed enum casesThese are internal details — if you only use the public facade and TestResponse assertions, no action is required.
See UPGRADE.md for the full list.
Four new artisan commands provide visibility into your spec and its relationship to your application.
spectator:validate — lint a spec file before tests run:
php artisan spectator:validate --spec=Api.v1.yml
php artisan spectator:validate --spec=Api.v1.yml --format=json
spectator:coverage — list every operation defined in the spec:
php artisan spectator:coverage --spec=Api.v1.yml
spectator:routes — cross-reference spec operations against your registered Laravel routes:
php artisan spectator:routes --spec=Api.v1.yml
Outputs matched, unimplemented, and undocumented routes at a glance.
spectator:stubs — generate skeleton test classes from a spec, ready to fill in:
php artisan spectator:stubs --spec=Api.v1.yml --output=tests/Contract
Operations are grouped by tag (falling back to path segment), producing one test class per group with one markTestIncomplete method per operation.
All commands support --format=json for machine-readable output.
SpectatorExtension is a PHPUnit 11 extension that tracks which spec operations are exercised across the full test run and prints a summary table when the suite finishes. Configure a minimum threshold to fail CI when coverage drops:
<extensions>
<bootstrap class="Spectator\Coverage\SpectatorExtension">
<parameter name="min_coverage" value="80"/>
</bootstrap>
</extensions>
Validation errors can now be emitted as structured JSON instead of ANSI-coloured text — useful for CI log parsers, LLM-driven workflows, and anything that processes test output programmatically:
SPECTATOR_ERROR_FORMAT=json
Or toggle per test:
Spectator::useJsonErrors();
Spectator::useTextErrors();
A failed assertion with JSON errors produces:
{ "errors": ["The data (null) must match the type: string"] }
Spectator::withPathPrefix('v1');
As an alternative to setting SPECTATOR_PATH_PREFIX in .env.
The combination of spectator:validate --format=json, SPECTATOR_ERROR_FORMAT=json, and SpectatorExtension makes Spectator a natural fit for AI-driven development workflows — structured outputs at every stage mean errors and coverage gaps can be piped directly into LLM toolchains.
Nullable inside additionalProperties not migrated — OpenAPI 3.0 nullable: true on dictionary value schemas was silently skipped during the 3.0 to 3.1 nullable migration. Values like null were incorrectly rejected. (#215)
Comma-separated values in header parameters — explode: false with type: array on in: header parameters now correctly splits the comma-separated string into an array before validation, matching the existing behaviour for query parameters. (inspired by #213)
TypeError with empty request body on allOf schemas — sending an empty body against a required allOf request body no longer throws a TypeError. The error is now a clean "Request body required!" validation failure. (#216)
Nullable properties inside allOf — nullable: true on properties nested within allOf arms is correctly migrated to 3.1-style type: [T, null]. (#212)
ValidationMode, Format, Source)readonly properties, and match expressions used where appropriatehttps://github.com/hotmeteor/spectator/compare/v2.5.2...v3.0.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v2.2.1...v2.3.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v2.2.0...v2.2.1
Full Changelog: https://github.com/hotmeteor/spectator/compare/v2.1.2...v2.2.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v2.1.1...v2.1.2
Full Changelog: https://github.com/hotmeteor/spectator/compare/v2.1.0...v2.1.1
Full Changelog: https://github.com/hotmeteor/spectator/compare/v2.0...v2.1.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.11.0...v2.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.9.0...v1.11.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.9.1...v1.10.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.7.0...v1.9.1
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.8.0...v1.9.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.7.1...v1.8.0
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.7.0...v1.7.1
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.6.0.1...v1.7.0
Small fix in test.
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.5.1...v1.6.0
$path param by @pH-7 in https://github.com/hotmeteor/spectator/pull/105Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.5.0...v1.5.1
Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.4.1...v1.5.0
formatSchema function does not return array by @jransijn in https://github.com/hotmeteor/spectator/pull/83Full Changelog: https://github.com/hotmeteor/spectator/compare/v1.4.0...v1.4.1
The Spectator middleware will now be prepended to the middleware stack vs appended so that models are not bound, and specs can match on primitive types.
This release fixes the parsing of route parameters.
Error rendering has been greatly improved so that now all validation issues are displayed:

How can I help you explore Laravel packages today?