adamquaile/behat-command-runner-extension
Behat extension that runs shell commands as part of your test flow, letting you trigger database migrations, fixture loading, and other setup or teardown tasks automatically before and after scenarios via configurable hooks and a command runner service.
Install as a dev dependency:
composer require --dev adamquaile/behat-command-runner-extension
Enable the extension in behat.yml:
default:
extensions:
AdamQuaile\Behat\CommandRunnerExtension\Extension: ~
Look first at:
vendor/adamquaile/behat-command-runner-extension/README.mdvendor/adamquaile/behat-command-runner-extension/src/behat.yml shipped with the packageConfirm the exact config keys for hook names such as beforeSuite, afterSuite, beforeScenario, afterScenario, beforeStep, and afterStep. The package is small, so reading the extension class is usually faster than guessing from docs.
First practical use case: reset the application database before every scenario.
default:
extensions:
AdamQuaile\Behat\CommandRunnerExtension\Extension:
beforeScenario:
- "php bin/console doctrine:database:drop --force --if-exists"
- "php bin/console doctrine:database:create"
- "php bin/console doctrine:schema:create"
Run a single feature to verify the hook fires before writing more commands:
vendor/bin/behat features/example.feature
Use suite hooks for expensive setup. Put cache warmup, asset builds, migrations, and container compilation in beforeSuite rather than beforeScenario.
Use scenario hooks for isolation. Reset fixtures, truncate tables, clear test directories, and reset queues before each scenario. Use afterScenario for cleanup that must run even after failures.
Keep profiles environment-specific. Define separate Behat profiles for local, CI, and debugging. Put destructive or slow commands only in the profiles that need them:
ci:
extensions:
AdamQuaile\Behat\CommandRunnerExtension\Extension:
beforeSuite:
- "php bin/console doctrine:migrations:migrate --no-interaction --env=test"
afterSuite:
- "php bin/console cache:clear --env=test"
Wrap complex logic in a shell or PHP script. Instead of long inline commands, call a script from the hook. This keeps YAML readable and lets you add conditionals, logging, retries, and tag-aware behavior that the extension itself may not provide.
beforeScenario:
- "php tests/behat/reset-database.php"
Pass environment explicitly. Do not rely on the parent process environment being identical in local and CI runs:
beforeSuite:
- "APP_ENV=test php bin/console cache:clear"
Use idempotent commands. Prefer --force --if-exists, --no-interaction, and commands that can safely run twice. This reduces failures when a previous run aborted mid-cleanup.
Separate setup and teardown intentionally. A common pattern is:
beforeSuite:
- "php bin/console doctrine:database:create --if-not-exists --env=test"
- "php bin/console doctrine:migrations:migrate --no-interaction --env=test"
beforeScenario:
- "php tests/behat/truncate-database.php"
afterScenario:
- "php tests/behat/clear-test-artifacts.php"
afterSuite:
- "php tests/behat/collect-logs.php"
Integrate with CI by making failures visible. Ensure commands write useful output to stdout/stderr, and run Behat with verbose output when debugging:
vendor/bin/behat -vvv
cd or call scripts using absolute paths.set -e in a shell script.:, #, {, }, or leading * can be parsed incorrectly. Quote command strings, especially when using shell operators.beforeScenario commands make suites slow. Database drops and container rebuilds before every scenario are easy to write but expensive. Prefer transactions, truncation, or suite-level setup where possible.rm -rf, &&, and $(pwd) may fail on Windows. If Windows developers run the suite, use PHP scripts or portable commands.composer.lock and review changes when updating. If you need custom behavior, the extension points are likely Behat’s own hook system or a thin wrapper script rather than extending the package itself.How can I help you explore Laravel packages today?