behat/web-api-extension
Abandoned Behat 3 extension (never had a stable release) that provides testing for JSON APIs. It won't receive further updates, so consider alternatives for new projects; kept here for reference and legacy suites.
Architecture fit. behat/web-api-extension is a Behat 3 extension that supplies reusable step definitions and a context for driving JSON APIs over HTTP. Architecturally it sits outside the Laravel application: it is a black-box acceptance-test layer that talks to a running endpoint, not an in-process test harness. It does not integrate with Laravel's service container swap, RefreshDatabase, facades, event fakes, queue fakes, mocks, or $this->getJson() / assertJson() assertions. That is the central architectural fact a TPM must internalize: adopting this package means running two parallel testing paradigms (Gherkin-driven black-box API specs alongside PHPUnit/Pest in-process feature tests), each with its own data-seeding, auth, and cleanup story. For a Laravel-first shop the fit is low; for a team already standardized on Behat and needing a live-environment JSON API smoke layer, the fit is modest but real.
Integration feasibility. Technically installable — composer require --dev behat/behat:^3.0 plus behat/web-api-extension:dev-master, register the extension and context in behat.yml, set base_url, write a feature file. The package ships a doc/index.rst that is the authoritative source for exact step vocabulary and config keys; the README is only a pointer. Feasibility is therefore bounded by discovery cost: a TPM should budget a spike to read the RST and source and confirm the actual step phrasing before committing any estimation to a roadmap. The realistic effort is small (days, not weeks) but front-loaded with unknowns, because there is no stable tag to pin semantically — you pin a commit hash.
Technical risk. High and asymmetric, for four reasons:
symfony/*, guzzlehttp/guzzle, and PSR interfaces. On a modern Laravel project, composer update will likely fail — and it will fail for the whole project, not just the test dependency. This is the risk most likely to convert a "small test-tooling decision" into a blocked-sprint incident.Key questions to resolve before any commitment.
composer.lock survive adding this package without forcing unrelated upgrades? (Spike this first — it is the cheapest kill-test.)Stack fit. Weak on a greenfield Laravel stack, and the TPM position should be that the default is do not adopt. Laravel's HTTP testing utilities with PHPUnit or Pest deliver the same assertion surface (status, JSON shape, headers) in-process, with framework alignment, zero abandonment risk, and no second configuration language. Where the package has a defensible niche is a narrow legacy context: an existing Behat 3 suite covering an internal, low-risk JSON API, with no migration budget this cycle and an accepted, documented security posture. That is a real but small addressable case, and the assessment should say so explicitly rather than letting enthusiasm for BDD framing widen the scope.
Migration path. Two directions, and a TPM must be clear which one is being approved:
class ApiContext extends WebApiContext) that owns auth, fixtures, and domain steps. Register only the subclass to avoid ambiguous step definitions. Keep all domain assertions in our own contexts and treat the package purely as request/response plumbing. Deliver a written exit plan with a target date and a scheduled replacement spike.Either way, the durable artifact is the Gherkin. Keep feature files free of extension-specific phrasing so that when the package is replaced — and it will be — only the context implementation and a handful of step definitions need rewriting.
Compatibility. Two hard constraints to put on the plan as acceptance gates: (a) PHP runtime version pinned in CI and documented; (b) the full dependency graph pinned, not just this package. Composer will emit an abandoned-package notice on install — suppress it deliberately and locally, never globally, so it does not mask other warnings. Confirm the extension's config key via getConfigKey() in the extension class before debugging YAML indentation, and verify env-var interpolation for base_url works on the pinned Behat 3 point release (otherwise generate behat.yml per environment in CI). Known sharp edges to schedule explicit test time for: trailing-slash URL joining, TLS verification against self-signed local endpoints, and duplicate step definitions if both the bundled context and a subclass get registered.
Sequencing. Run it as a time-boxed decision, not an open-ended evaluation:
composer update forces unrelated upgrades, stop — recommendation is reject, and the cost is one engineer-day.doc/index.rst and confirm the actual step vocabulary and config keys from source. Do not estimate against the README.Maintenance. Total cost of ownership is unfavorable and should be stated plainly to stakeholders. There are no upstream fixes, so every future breakage — a PHP minor bump, a Guzzle CVE, a Symfony constraint conflict — becomes our incident to patch, in a forked or vendored copy. Budget the real overhead: someone must fork or vendor the source into our VCS, document the decision, and own a periodic review. That is a standing tax on a test-only tool with no product return, which is precisely the kind of cost that quietly accumulates and is never retired.
Support. There is none, and there will not be. No vendor accountability, no SLA, no roadmap, no issue triage, no upgrade guide. Internal support means the named owner reverse-engineers thin documentation. This is acceptable only where the API under test is internal and low-risk; it is disqualifying anywhere a regulator, a customer contract, or an on-call rotation could ask "who fixes this."
Scaling. Poor on two axes. Team scale: the step vocabulary is fixed and undocumented outside the RST, so onboarding depends on one or two people who have read the source — bus factor of one or two. Suite scale: step-definition collisions and extension-point limits (no plugin hooks, no PSR-18 client injection) mean large suites tend to accrete custom contexts that eventually diverge from the bundled behavior, at which point the package contributes little beyond an HTTP wrapper. Estate scale: mixed frameworks increase cognitive load and the "which framework does this test belong in?" tax on every new test written.
Failure modes to plan for.
-vvv plus a temporary @AfterScenario hook that dumps last response body and headers.Ramp-up. Low for anyone who already writes Gherkin and Behat 3 config; effectively zero transferable knowledge for engineers who only know PHPUnit/Pest, and it teaches patterns that do not carry forward. Realistically one to two engineer-days to a first green scenario, most of it spent reading doc/index.rst and source. Plan for a short internal write-up: the exact step vocabulary, the config keys, the fork location, and the replacement target — otherwise the knowledge walks out with the first engineer.
TPM recommendation. Reject for new work and for any Laravel-first team; treat it as a reference implementation for expressing API acceptance criteria. Where a legacy Behat 3 suite genuinely exists and cannot be migrated this cycle, permit a narrow, time-boxed bridge under five explicit gates: test-only dependency, pinned PHP runtime, pinned dependency graph, named owner, and a written exit plan with a scheduled replacement spike. In all cases, convert the outcome into a reusable dependency-intake policy — minimum release stability, maintenance signals, successor plan — so the next archived package is excluded by rule rather than by re-evaluation.
How can I help you explore Laravel packages today?