pestphp/pest-plugin-agent
Pest Plugin Agent extends Pest with agent-focused testing helpers and expectations, making it simple to write expressive, reliable tests for AI agent workflows in Laravel and PHP projects. Integrates with Pest's familiar syntax for confident agent testing.
What the TPM is actually buying. Not a test framework — a one-command verification primitive. vendor/bin/pest --agent='<php snippet>' writes a temporary it('verify', function () { … }) file, runs it under the project's existing Pest configuration, and deletes it. The snippet is a raw PHP body: no <?php, no use imports, every class fully qualified. That is the whole product surface. Everything else the TPM needs to manage is convention, guardrails, and measurement.
Architecture fit. Additive and non-invasive. It does not alter the runner, register a service provider to operate, or add a config layer. It reuses tests/Pest.php uses()/namespace bindings, so factories, actingAs(), Mail::fake(), seeders, and (with pest-plugin-browser + Playwright) real browser flows all apply unchanged. Strong fit for Laravel + Pest repos that already have test bindings and factories. Poor fit for PHPUnit-native or non-Laravel PHP, where the helpers it leans on do not exist. Do not bend the tool to fit those repos; scope the repos.
Why this matters strategically. It is the cheapest available answer to "how does an agent prove its own work?" and it collapses write-temp-test → run → clean-up into one atomic command. If agent-authored PRs are anywhere on the roadmap, this is the verification step underneath them. The adoption signal it generates — the percentage of agent tasks that self-verify before handoff — is a leading indicator of how much human review you can retire. That is the leverage; the plugin itself is small.
Integration feasibility. Genuinely low effort: one dev dependency, no config, no parallel harness, no migration of existing tests. The real setup cost is operator discipline, and the bundled SKILL.md is a tell that the maintainers already know where agents fail:
$user to "" before PHP sees it — a silent break that looks like a real failure, or worse, a weakened check that passes. Single outer quotes only; never hand-escape \$.User becomes Class "User" not found. Every class needs a leading backslash.' anywhere terminates the outer shell quote even inside PHP double quotes; the documented escape hatch is write-a-.php-file then --agent="$(cat …)".onMobile() does not exist; the chain is ->on()->mobile(). waitForText() is deprecated. Undocumented methods fail.RefreshDatabase. Pointed at a persistent database it wipes it. The commented-out-by-default line is a guardrail, not an oversight.Technical risk. Seven stars, zero dependents, a 0.295 score, single-maintainer bus factor, no published version-support matrix for Pest/PHP/Laravel, and browser-API churn already visible in the docs. Plus, by design, --agent executes arbitrary PHP inside a bootstrapped application — that is dev/CI-only surface and must be treated as such. Acceptable for a dev dependency you can rip out in an afternoon; unacceptable if it becomes load-bearing infrastructure.
Key questions to put to the maintainer before pilot.
--agent a stable API?Stack fit — be explicit about in-scope and out-of-scope. In scope: Laravel applications on Pest with tests/Pest.php bindings configured, factories and seeders in place, and an in-memory or otherwise expendable test database. Browser verification is a separate, optional layer via pestphp/pest-plugin-browser plus npx playwright install. Out of scope: PHPUnit-native repos, repos without Pest uses bindings, repos without factories or seeders, and anything production-adjacent. Naming the exclusions up front prevents the pilot from drifting into a re-platforming exercise.
Migration path. There is no migration — nothing is replaced. Treat it as greenfield, additive, and fully reversible. Land it as a pinned require-dev dependency on one pilot repo (or a dedicated branch) with a nominated agent-owner. Ship a short internal runbook derived from SKILL.md: single outer quotes, FQCN everywhere, documented browser API only, the apostrophe fallback, screenshot hygiene, and "do not flip RefreshDatabase without asking." That runbook, not the plugin, is the durable artifact.
Compatibility checks before rollout. Validate Pest major, PHP, and Laravel against the plugin. Confirm Playwright exists on developer machines and in CI, or scope browser verification to local-only for the pilot. Document that the in-memory DB starts empty every run (seed inline with $this->seed(...)), that screenshots are hardcoded to tests/Browser/Screenshots/ and belong in a gitignored subfolder with periodic cleanup, and that uses bindings resolve regardless of snippet file location — a scratch path outside tests/ still gets Feature/Browser traits. Prefer a containerized dev environment to isolate the code-execution surface.
Sequencing.
--agent out of production and release pipelines — dev/CI only.Encode the product boundary now. The docs are explicit that this is a probe, not a replacement for committed tests. Institutionalize that in agent guidelines: probes answer "does this work right now"; real tests answer "this must never break again." Without that rule, teams will ship agent-verified code with zero regression coverage and call it done — the single most likely way this investment goes wrong.
Maintenance. Small, low-friction dependency with a low activity signal. Pin the version, subscribe to upstream releases, and budget periodic upgrade work when Pest majors move. The dominant ongoing cost is documentation debt: SKILL.md is effectively your runbook and must be curated and versioned alongside the plugin, because every new agent and engineer reads it and every drift in Pest or the browser plugin invalidates parts of it. Assign a named owner for that document, not just the dependency.
Support. No vendor. Upstream is a single maintainer on a young package. Internal support means one engineer who understands the temp-file mechanics, the quoting trap, and the browser plugin's API surface. If that person leaves, the fallback is trivial — the pattern is a Composer or shell script — so the risk is schedule, not capability. Say that out loud when you pitch it; it is why the bet is cheap.
Scaling. Scales linearly with the number of repos you enable, not with test volume. Cost drivers: Playwright provisioning on every machine that runs browser probes, screenshot artifact churn if design-review loops become routine, and CI minutes if agents self-verify frequently. Cap that with convention — delete throwaway screenshots, gitignore retained ones, set expectations on verification runs per task. Parallel test execution is unverified against this plugin; confirm before enabling it in a parallelized CI matrix.
Failure modes to plan for.
$variables; a login check silently degrades to a weaker assertion. Mitigate with the runbook, snippet review, and tracking of false-negative rates.--agent runs PHP inside a bootstrapped app. Dev/CI only, sandboxed DB, never production-adjacent.RefreshDatabase against a persistent database wipes it. Guardrail is process, not code — call it out in the enablement doc.Ramp-up. Hours, not days, for an engineer already fluent in Pest — the friction is conventions, not concepts. Budget roughly a half-day to read SKILL.md, run the backend and browser examples, and internalize the quoting rule; more if Playwright has to be installed and debugged per machine. Agents ramp faster than humans because the skill file is written for them, which is precisely the point: the plugin is documentation-first tooling.
Recommendation. A contained trial on one or two Pest repos with an in-memory test database, measured against review-cycle time, before any broader rollout. Buy rather than build while you are on Pest and want upstream maintenance at near-zero cost; build a thin wrapper of your own the moment you need policy upstream will not carry — allowlists, sandboxing, audit logs, artifact retention. Either way, keep committing real regression tests alongside it. It verifies; it does not regress-guard.
How can I help you explore Laravel packages today?