wp-cli/wp-cli-tests
WP-CLI testing framework that bundles shared Behat, PHPUnit, PHP_CodeSniffer, and PHPStan tooling for WP-CLI commands and packages. Add it as a dev dependency to get ready-made test scripts, Behat feature contexts, and linting configs out of the box.
Verdict up front: this is a mislabeled candidate for a Laravel evaluation. It is a Composer-installable PHP dev dependency, so it installs into a Laravel repo without friction — but its substance is a WordPress/WP-CLI acceptance-testing harness. The reusable surface for a Laravel team is narrow: a set of vendor/bin runner scripts, a PHPUnit base TestCase, a PHPCS ruleset, a PHPStan extension for analysing PHP embedded in Gherkin docstrings, and two output-hygiene environment variables. Everything that makes the package valuable (Behat steps that install WordPress, create wp-config.php, provision a database, run wp commands) is inert outside a WordPress target.
Architecture fit. Three distinct layers, with very different portability:
| Layer | Portability to Laravel |
|---|---|
Runner scripts (run-phpcs-tests, run-phpstan-tests, run-php-unit-tests, install-package-tests) |
High — thin wrappers, usable as-is |
| Feature-file analysis (PHPStan + PHPCS over Gherkin docstrings) | Conceptually high, mechanically coupled — wired through shell scripts and a WP-CLI-tuned neon/ruleset |
Behat FeatureContext Given/When/Then steps |
Low — ~25 Given steps assume WordPress, MySQL, and a wp binary on PATH |
Integration feasibility. Install is a one-liner; adoption is not. The README's step 2 instructs you to define test, lint, phpcs, phpstan, phpunit, and behat keys in composer.json. In a typical Laravel repo those exact keys already exist (Pint via lint, Pest/PHPUnit via test, Larastan via phpstan). This is a silent destructive merge, not an additive one — the single highest-risk item in the whole evaluation and the one a TPM must gate on before anyone runs composer require.
Technical risk.
Given a WP install step does a full WordPress download and install per scenario. There is no non-WordPress path through the functional suite.lint-gherkin needs Node.js ≥ 20 and shells out via npx, adding a second runtime to CI. Mitigated by design: missing npx reports a skip rather than failing, which is the right call but also means the lint silently never runs if nobody checks.phpcs/feature-files.sh; Windows requires the extra behat.yml step. Cross-platform CI matrices get complicated."process-timeout": 1800. Confirm how your existing config block merges before assuming it took effect.composer.json scripts call global binary names from vendor/bin. If a future release renames or drops a runner, your CI fails with a confusing error rather than a dependency-resolution error. Pin exactly.composer phpcs -- src/) skips feature-file blocks entirely. Both are quiet-degradation modes.Key questions for the maintainers / your own team:
wp-cli command package, a WP-backed integration? If no, this evaluation should stop here.composer.json script keys (test, lint, phpcs, phpstan, phpunit) would this overwrite, and what breaks if it does?FeatureContext be consumed partially — i.e. only the generic CLI steps (I run ..., STDOUT should contain) without the WordPress Given steps? Is the class monolithic or are contexts composable?WP_CLI_CS configurable enough to approximate PSR-12/Laravel conventions, or does it hard-fail on framework idioms (facades, Eloquent, service providers)?Stack fit. Poor as an aggregate; useful as a parts bin. Do not adopt it as "our test framework." Adopt at most two artifacts, and only if the answers to Key Questions 1 and 3 are favourable.
Recommended path — a two-week, time-boxed spike, not a migration:
composer.json. Rename ours or theirs — but decide explicitly. Never let the install edit scripts blind.require --dev inside that package only..feature file. If we have any Gherkin at all, this is the reason to be here.FeatureContext composability. If the WordPress steps are separable, adopt the generic CLI step set and write our own Given layer. If not, abandon the Behat half.Compatibility notes to settle during the spike:
behat.yml and FeatureContext match our Behat major; Behat has a history of breaking context/step-discovery changes.WP_CLI_CS will not coexist peacefully with Pint. Running composer phpcbf with the WP-CLI standard against Laravel code produces large, unwanted diffs. Isolate it to the spike package or drop the phpcs/phpcbf scripts entirely.phpstan/feature-files.neon defaults are tuned for WP-CLI fixtures. Budget time for an ignore baseline; the README warns you will need one, and correctly frames snippets as fixtures rather than production code.Sequencing (if the spike succeeds):
Collision audit → isolated-package install → static-analysis half only → evaluate Behat half → one CI job → team review → expand or revert.
Strictly one stage per gate. If the collision audit produces a "no clean answer," stop — the cost of the workaround will exceed the value of a 42-star dev dependency.
Fallback (likely the right answer for most Laravel teams): decline adoption, and instead port the idea — extract PHP docstrings from Gherkin, pad line numbers, feed to PHPStan — into a ~150-line internal script. You get the only genuinely novel piece, keep one toolchain (PHP + Pest/Larastan/Pint), and take on no external dev-dependency risk.
Maintenance. Maintained by the WP-CLI organisation, which is a real and active project, but this specific package is small-audience infrastructure — 42 stars, effectively zero registered dependents, a single-digit contributor base. Bus factor is the concern, not abandonment: it is load-bearing for WP-CLI's own packages, so it will be maintained for them, and their priorities are the only ones that count. MIT license removes legal risk; it does not remove the "we depend on someone else's dev workflow" risk. Pin and vendor-review on every bump.
Support. Community channels (GitHub issues, WP-CLI Slack). No SLA, no commercial backing, no security-response process documented. For a dev-only dependency that runs in CI rather than production, this is acceptable — but it must be recorded in the risk register as an unsupported dependency, not quietly absorbed.
Scaling. This is the dominant operational cost and the reason to be sceptical. The functional suite installs WordPress from scratch per scenario, and the project's own contributor guide states a full composer test takes "tens of minutes" even on packages it was designed for. Requirements are non-trivial: jq, a MySQL/MariaDB client, a pre-provisioned test database, and network access to WordPress.org. Parallelisation is not first-class. The package does mitigate well where it can — WordPress version metadata cached a day in temp, core ZIPs cached by content hash, WP_CLI_TEST_WP_VERSION_CACHE_TTL to tune it — but database provisioning and core install remain the wall-clock floor. For a Laravel team already spending 5–8 minutes on a Pest suite, a 20–40 minute acceptance stage is a process change, not a tooling change. Budget CI minutes, not just engineering hours.
Failure modes to instrument:
| Failure | Symptom | Detection |
|---|---|---|
| Script-key overwrite | Our lint/test run the wrong thing |
Diff composer.json in the PR — mandatory review gate |
| WordPress.org unreachable | Behat Given steps fail; cache falls back to last-known |
Alert on the cache-staleness path |
| No Node in CI | lint-gherkin skips silently |
Assert the lint actually ran; a skip is not a pass |
| PHPStan parse failure | Whole analysis aborts on one bad docstring | Package skips non-standalone blocks — verify the skip list is reviewed, not ignored |
| Stale ignore patterns | Errors silently suppressed | Periodic audit; a pattern matching nothing is never reported |
| Narrowed invocation | composer phpcs -- src/ skips feature-file blocks |
Don't narrow in the CI job |
| CI log pollution | ANSI escapes and progress bars in captured output | Set NO_COLOR=1 and WP_CLI_TEST_QUIET=1 permanently in the environment |
The quiet-mode and NO_COLOR variables are genuinely well-designed and worth copying into our own tooling regardless of the adoption decision — compact file:line:col output is exactly what an agent-read or log-parsed CI wants.
Ramp-up. Moderate-to-high for the full framework, low for the static-analysis half. Behat/Gherkin is a distinct literacy from Pest; a team without it needs roughly one to two sprints before feature files are written idiomatically rather than as disguised PHPUnit tests. The step library is broad and well-documented in agents/AGENTS.md, which lowers the floor — but every WordPress-flavoured step is a step our team cannot use, which inflates apparent ramp-up while depressing realised value.
Recommendation. Reject as a wholesale framework. Run the two-day spike on the feature-file static-analysis piece only, in one isolated package, gated behind the script-collision audit. If the spike shows clean composability with our existing PHPStan/Pest setup, keep that half and discard the rest. If it does not — and the default expectation is that it will not — port the extraction concept internally and take on no dev-dependency risk at all.
How can I help you explore Laravel packages today?