Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Openapi Contract Testing Laravel Package

studio-design/openapi-contract-testing

Validate Laravel/PHP API responses and requests against your OpenAPI spec with contract tests that catch breaking changes early. Supports schema validation, status codes, and CI-friendly assertions.

View on GitHub
Deep Wiki
Context7

Getting Started

Gesso is a spec/contract-testing toolkit for Laravel + PHPUnit/Pest. The fastest path from zero to a passing contract suite in 2.5+:

composer require --dev studio-design/gesso
vendor/bin/gesso doctor --emit-config

doctor is the first thing to run and, as of 2.7.0, it can emit an equivalent gesso.php (--emit-config, #627). Use that generated file as the baseline rather than hand-writing config — it reflects what the doctor actually detected about your app, adapters, and spec location.

Where to look first, in order:

  1. gesso.php — since #593 a single config is shared between the PHPUnit extension and the Laravel integration. There is no longer a "PHPUnit side" and a "Laravel side" to keep in sync; if you find yourself duplicating keys, you're on an old layout.
  2. The specs list in config — the Laravel integration now reads the spec list from config (#621) instead of relying on test-side registration.
  3. Contract namespace — named contract checks moved here in 2.7.0 (#608). Import from StudioDesign\Gesso\Contract\…; the old Fuzz alias still resolves but is deprecated.
  4. vendor/bin/gesso --help — every subcommand now accepts --format and --output-file (#620), so output handling is uniform across doctor, coverage:*, and the rest.

First use case: take one Laravel route you already trust, declare its spec in gesso.php, and write a single test that calls the endpoint and asserts the response against the contract. Once that is green, run gesso doctor again — it will report route-parity gaps, which is the natural next workload.

Implementation Patterns

Config-first, test-second. The 2.7.0 theme is clearly "stop configuring things in test bodies." Route-side parity exclusions (#621), adapter policy hooks (#614), coverage gate thresholds (#626), and the spec list all now default from gesso.php. Write tests that say what is being asserted, and put which spec/policy/threshold in config.

Consumer-side contract testing with HTTP client fakes. The headline feature in this release (#617) is spec-driven fakes: you can test code that calls an external API without a live provider. Pattern:

// The fake is generated from the spec, so the request shape is validated
// against the contract before your code ever sees a response.
$fake = Gesso::fakeClient('payments.yaml', 'createCharge');

$fake->assertRequestMatchesSpec();
$this->postJson('/orders', [...])->assertOk();

This is the feature most teams will actually feel day to day — it turns gesso from "spec linter" into "boundary test harness."

Adapter policy hooks. If you use Symfony HttpClient or PSR-7 messages anywhere in the request path, the policy hooks now inherit from shared config (#614). Declare them once:

'adapters' => [
    'symfony' => ['policy' => ['follow_redirects' => false]],
    'psr7'    => ['policy' => ['strict_body' => true]],
],

…rather than re-declaring per test class.

CI wiring. With coverage:merge and coverage:gate defaulting from gesso.php via --config (#626), your pipeline can be two commands plus the test run:

vendor/bin/gesso coverage:merge --config=gesso.php --output-file=build/coverage.json
vendor/bin/gesso coverage:gate  --config=gesso.php --format=github

--format on every subcommand means you can emit JSON for artifact upload and a human format for the job log from the same invocation shape.

Enum drift under paratest. Enum drift evaluation is now hoisted to once per paratest run (#613). This pairs naturally with pest --parallel: you get drift checking without paying the cost per worker.

Form and multipart payloads. Validation now honors encoding style, explode, and part headers (#619), so application/x-www-form-urlencoded and multipart/form-data endpoints are testable to the same depth as JSON. Stubs also support explicit request prefixes (#592) — use these for path-parameter endpoints where the generated prefix would otherwise collide.

Gotchas and Tips

Migrate off the Fuzz alias now. #608 moved named contract checks to Contract and left Fuzz as a deprecation shim. It will be removed; do the find-and-replace while the diff is small.

JSON bodies are decoded as objects. #561 fixes nested {} being read as []. The behavior change is real: assertions that previously passed against an empty array for an empty object will now see a stdClass. If you have assertions like assertSame([], $body->meta), they will flip. Grep for empty-array expectations on decoded bodies.

Enum and setting values are normalized. Config enum values are canonicalized like extension parameters (#609), and setting values are lower-cased without requiring ext-mbstring (#570). Practical consequence: don't encode meaning in the case of a config value — "JSON" and "json" are the same setting. The mbstring fix also means the package now behaves identically on minimal CI images.

Body boundaries are preserved across adapters and doctor (#566). The inverse is the trap: if you hand-assemble a multipart body in a test instead of letting gesso build it, you can still produce a payload the validator rejects. Build payloads through the package's helpers.

doctor --emit-config is your upgrade diff. After bumping to 2.7.0, emit a fresh config and diff it against yours. Given the volume of config-driven defaulting in this release, a stale key that used to be read from extension parameters may now be silently ignored.

Remove parallel workarounds. #599 makes the Pest --parallel orchestrator tolerate the sealed event facade. If you added --parallel exclusions or a bootstrap shim to dodge that, they're now dead weight — and dead weight in a test bootstrap is the kind of thing that bites six months later.

Enum drift caches per paratest run. Once-per-run evaluation is a performance win but means drift introduced during a run (watch mode, long-lived dev loop) won't be re-detected. If you run gesso in a watcher, force a fresh run rather than relying on the cached result.

Static analysis pinning. #600 pins gesso's dev analysis tooling alongside phpstan 2.3 fixes. If you extend internals or ship a custom adapter, your project's phpstan version may differ from the pinned one — expect to reconcile rules rather than assume parity.

Extension points worth knowing: adapter policy hooks (config-driven, per #614), --format for custom output handling, and doctor --emit-config as a programmatic way to introspect the resolved configuration.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle