friends-of-behat/performance-extension
FriendsOfBehat PerformanceExtension adds performance monitoring to Behat, measuring scenario runtimes and enforcing time limits so slow tests or regressions fail early. Useful for PHP teams wanting performance checks alongside behavioral tests.
composer require --dev friends-of-behat/performance-extension
Register it in behat.yml (top-level extensions block, not under a suite):
default:
extensions:
FriendsOfBehat\PerformanceExtension: ~
Run your suite twice — first to warm whatever cache the extension manages, second to measure the benefit:
time bin/behat --no-interaction --format=progress
Where to look first:
vendor/friends-of-behat/performance-extension/README.md — the actual supported config keys (the package is small and lightly documented).vendor/friends-of-behat/performance-extension/src/ServiceContainer/PerformanceExtension.php — the entry point that wires compiler passes / service overrides into Behat's container.composer.json — check the pinned behat/behat and symfony/* constraints before adopting, since this tells you immediately whether it will even install on your stack.First use case: a medium-to-large Behat 3 suite where startup (container compilation + autoloading) dominates wall-clock time. This extension targets that bootstrap cost by using PHP 7-only language features to take shortcuts in Behat's container/loading path — it does not make scenarios themselves run faster. Measure startup in isolation (bin/behat --dry-run or a suite with a single trivial scenario) to see whether the win is meaningful for you.
Baseline before/after, on the same machine. Use hyperfine 'bin/behat --no-interaction' or three time runs each side; Behat startup is noisy and a single run will mislead you. Record both the first (cold) and second (warm) invocation — if the extension caches, only the warm number reflects steady state.
Scope it to profiles you control. Enable it in your default/ci profiles, not in shared profiles consumed by environments you don't own:
default:
extensions:
FriendsOfBehat\PerformanceExtension: ~
ci:
extensions:
FriendsOfBehat\PerformanceExtension: ~
Cache the cache directory in CI. Whatever directory the extension writes to, include it in your CI cache keyed on composer.lock + behat.yml + any custom extension source, so it's invalidated when the container inputs change:
# .github/workflows/behat.yml (sketch)
- uses: actions/cache@v3
with:
path: var/behat-cache # verify the real path from the README/source
key: behat-${{ hashFiles('composer.lock', 'behat.yml') }}
Stack other bootstrap wins on top. This package was never a silver bullet; pair it with:
composer dump-autoload -o # optimized autoloader
php -d opcache.enable_cli=1 bin/behat
and Behat's own --no-snippets, --no-paths, --format=progress flags.
Fork-and-pin when you need a tweak. Because it's archived with zero dependents, upstreaming isn't an option. If you need behavior changes, use a VCS or path repository in composer.json pointing at your fork, and pin the exact commit — don't let a ^1.0 constraint silently resolve to something unmaintained.
"repositories": [
{ "type": "vcs", "url": "[email protected]:your-org/performance-extension.git" }
]
It's archived (last release 2016-11-02) and has zero dependents. Treat it as a reference implementation, not infrastructure. Pin an exact version, vendor-review the code, and budget time to remove it if it breaks. There is no upstream to file issues against.
Hard PHP 7 dependency. The whole premise is "features available only for newer PHP versions," so it will not install or run on PHP 5.6, and on PHP 8 you should expect deprecations or hard failures from reflection/type APIs that changed after 7.x. Validate on your exact CI PHP version before committing.
Behat and Symfony version lock-in. It was written against Behat 3.x and the Symfony 2/3 DI component. If your project has since moved to a newer Behat or Symfony, the compiler passes and service IDs it decorates may no longer exist — you'll typically see "service not found" or a container compile error, not a graceful fallback.
Container cache invalidation is the classic trap. If the extension caches a compiled container, edits to behat.yml, newly installed extensions, or changed environment variables may not be picked up. When behavior looks "stuck," clear the cache directory first, before debugging anything else. Confirm what options the extension exposes for a cache path (check the README and PerformanceExtension.php) so you know exactly what to wipe.
Verify the extension is actually loaded. YAML indentation errors in the extensions block fail silently. Run:
bin/behat -vvv
bin/behat --config-reference # dumps resolved config; look for your extension
If it's absent from --config-reference, it isn't wired up — don't trust the timing numbers yet.
Some extensions don't compose well with cached containers. Any extension that mutates the container at runtime (rather than via compiler passes) can behave differently when the container is reused across runs. If a third-party Behat extension starts failing only in CI with this installed, disable PerformanceExtension first to isolate.
Extension points are minimal by design. It hooks Behat's service container and bootstrap; there's no plugin API to speak of. The realistic extension strategies are: (a) fork it, (b) copy the compiler pass into your own small extension, or (c) drop it entirely and lean on Composer's optimized autoloader, OPcache CLI, and Behat's own flags — which, on modern PHP, usually captures most of the gain.
How can I help you explore Laravel packages today?