The ExploresOpenApiEndpoint trait generates N happy-path request inputs for one (method, path) operation directly from the OpenAPI spec — the PHP equivalent of Schemathesis. Pair it with the existing ValidatesOpenApiSchema trait and every fuzzed call automatically asserts response contract conformance and records coverage.
use PHPUnit\Framework\TestCase;
use Studio\OpenApiContractTesting\Laravel\ExploresOpenApiEndpoint;
use Studio\OpenApiContractTesting\Laravel\ValidatesOpenApiSchema;
use Studio\OpenApiContractTesting\Attribute\OpenApiSpec;
#[OpenApiSpec('front')]
class CreatePetTest extends TestCase
{
use ExploresOpenApiEndpoint;
use ValidatesOpenApiSchema;
public function test_create_pet_contract(): void
{
$this->exploreEndpoint('POST', '/v1/pets', cases: 50, seed: 1)
->each(fn ($input) => $this->postJson('/api/v1/pets', $input->body)
->assertSuccessful());
}
}What you get per case (Studio\OpenApiContractTesting\Fuzz\ExploredCase):
| Property | Description |
|---|---|
body |
Generated JSON body (or null when the operation has no application/json requestBody) |
query |
name → value for every in: query parameter |
headers |
name → value for every in: header parameter (excludes the OpenAPI-reserved Accept/Content-Type/Authorization) |
pathParams |
name → value for every {placeholder} segment |
method, matchedPath |
The resolved spec template (/v1/pets/{petId}) and its method |
The collection is Countable and IteratorAggregate, so foreach ($cases as $case) works too if you prefer it over the fluent each() helper.
- Supported keywords:
type(string/integer/number/boolean/object/array/null),enum,format(email/idn-email/uuid/date/date-time/time/uri/url/iri/hostname/ipv4/ipv6),minLength/maxLength,minimum/maximum,required,properties,items. - Optional object properties alternate between included and omitted across cases, so each batch exercises both required-only and required+optional shapes.
- Required keys are always emitted.
- Path resolution accepts both the spec template form (
/v1/pets/{petId}) and concrete URIs that match it (/api/v1/pets/123withstrip_prefixes=/api). Captured URI values are intentionally discarded —pathParamsis always regenerated from the operation spec for consistency.
When fakerphp/faker is installed (already a transitive dev dependency via orchestra/testbench for most projects), generation uses Faker's locale-aware primitives and is fully deterministic for a given seed:. Without Faker, the trait falls back to deterministic counter-based primitives that still pass schema validation — your CI never depends on a runtime-installed package.
# Optional but recommended for realistic generation
composer require --dev fakerphp/fakerThe MVP intentionally targets happy-path generation. Tracked separately:
- Boundary value injection (min/max-length extremes, Unicode edge cases)
- Negative-case generation (deliberately invalid inputs to assert 4xx responses)
oneOf/anyOf/allOfcomposition; regexpattern;multipleOf;minItems/maxItems- Whole-spec auto-exploration (
exploreSpec()to walk every endpoint)