Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phpunit Speedtrap Laravel Package

johnkary/phpunit-speedtrap

PHPUnit SpeedTrap reports slow tests and slow test suites so you can find and fix performance bottlenecks. Set a time threshold and it flags any test exceeding it, helping keep your suite fast.

Deep Wiki
Context7

Getting Started

1. Install as a dev dependency

composer require --dev johnkary/phpunit-speedtrap

2. Confirm your PHPUnit version before going further

composer show phpunit/phpunit | grep versions

This is the single most important step. SpeedTrap's latest tagged release (2021) ships a PHPUnit listener, and listeners were removed in PHPUnit 10. So:

  • PHPUnit 9.x (Laravel 9 and earlier, or Laravel 10/11 projects pinned to ^9.5) — works as documented.
  • PHPUnit 10/11 (default Laravel 10+/11) — the listener class will not be loaded; you need a maintained fork or to skip this package.

3. Register the listener in phpunit.xml

<phpunit ...>
    <listeners>
        <listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener">
            <arguments>
                <array>
                    <element key="slowThreshold">
                        <integer>500</integer>
                    </element>
                    <element key="reportLength">
                        <integer>10</integer>
                    </element>
                </array>
            </arguments>
        </listener>
    </listeners>
</phpunit>

4. Run your suite and look at the tail of the output

php artisan test
# or
vendor/bin/phpunit

After the normal result line you should get a "slow tests" block listing each test above the threshold, its duration, and a count of how many times it exceeded the threshold. If you see nothing, jump to Gotchas and Tips — silent non-configuration is the #1 failure mode.

First use case: run the full suite with slowThreshold at 1000 to get a short list of the genuinely bad offenders, fix the top 3, then ratchet the threshold down to 500.

Implementation Patterns

Start permissive, then ratchet. A 500 ms threshold on a legacy Laravel suite produces dozens of entries and the team stops reading the report. Begin at 1000–1500 ms, fix or annotate the top offenders, and commit a lower threshold only once the report is short. Treating the number in phpunit.xml as a reviewed budget (like a coverage minimum) is what makes the tool keep working after month three.

Per-test override for intentionally heavy tests. Anything that legitimately needs >1 s — PDF generation, spreadsheet exports, image manipulation, a seeded full-database import — should declare its own budget in the docblock rather than pollute the report:

/**
 * @slowThreshold 3000
 */
public function test_generates_quarterly_pdf_report(): void
{
    //
}

Add a one-line comment explaining why it's heavy. Otherwise the next developer "fixes" the annotation instead of the test.

Typical Laravel culprits and the fix each one implies

Symptom Usual cause Fix
300–800 ms on every DB test migrations running per test RefreshDatabase with a schema-dump/migration cache, or DatabaseTransactions
500 ms+ on model tests factories with ->create() and deep relationships build only what the test asserts; use make()/states; createMany
1 s+ sporadic real HTTP in a feature test Http::fake(), or move to a contract test
2 s+ Hash::make() at production bcrypt rounds BCRYPT_ROUNDS=4 in phpunit.xml env
Slow, uniform Storage::disk('local') writes Storage::fake()
Very slow, no obvious cause sync queue driver running listeners Queue::fake() unless the test is about the job

Iterate on one test at a time. Once SpeedTrap names an offender, measure in isolation:

vendor/bin/phpunit --filter test_generates_quarterly_pdf_report

Repeat after each change. SpeedTrap tells you what is slow, not why — for the "why" within a slow test, reach for Xdebug profiling or a few microtime(true) checkpoints temporarily.

CI vs local configuration. Keep two thresholds: a strict one locally (where hardware is predictable) and a looser one in CI (shared runners are noisy). Recent versions support environment-variable overrides for exactly this, so the committed phpunit.xml can hold the local value and CI can export the looser one — check the README for the exact variable names for your installed tag, and verify the report actually quotes your new number before trusting it.

Use it as a regression guard, not just a cleanup tool. Because the listener reports on every run, a threshold committed to the repo makes a newly-slow test visible in the same PR that introduces it. Pair this with PHPUnit's failure output in CI logs so reviewers see the slow-test block without digging.

Split the suite rather than annotating everything. If a meaningful chunk of tests are inherently slow, put them in @group integration / a separate phpunit.integration.xml and run SpeedTrap only against the fast suite during the inner dev loop. Annotations are for exceptions; groups are for categories.

Gotchas and Tips

  • The <array>/<element> nesting is mandatory. Writing <integer>500</integer> directly under <arguments> (a very natural-looking mistake) is silently ignored — SpeedTrap falls back to its defaults and you'll believe it's working while reporting against the wrong number. Verify by temporarily setting the threshold to 1 and confirming the report explodes with entries.
  • PHPUnit 10+ incompatibility is a hard stop, not a warning. If the listener class isn't autoloadable for your PHPUnit version you get no output at all, not an error. Check the installed tag's composer.json PHPUnit constraint first.
  • Units are milliseconds, integers. 0.5 or "500ms" will not do what you expect.
  • Timing includes setUp()/tearDown() for that test, so a slow setUp() shows up as a slow test method. setUpBeforeClass() overhead is amortised across the class and may be attributed in ways that surprise you — a class that looks uniformly slow often has one expensive bootstrap.
  • Data providers multiply rows. A fast test with 40 provider cases can appear as 40 separate lines; the report counts occurrences, so 12x next to a test usually means the provider, not repeated runs.
  • Docblock annotations require the docblock to be on the test method and require PHPUnit to still parse annotations. On PHPUnit 10+ (or with annotation parsing disabled) @slowThreshold is inert, and in Pest the docblock above it(...) may not be picked up reliably — prefer the global config value there.
  • Parallel runners distort the picture. With Paratest, each worker emits its own report and wall-clock times inflate under CPU contention. Disable parallelism when you're actually hunting regressions, or expect inflated numbers in CI.
  • CI performance is noisy. A test at 480 ms locally can exceed 800 ms on a cold shared runner. Set the CI threshold with 30–50 % headroom or you'll field false alarms weekly.
  • Don't drop below ~200 ms on Laravel. Bootstrapping the framework, the container, and a DB connection per test puts a floor under even trivial tests; a threshold below that turns the report into a copy of the suite.
  • SpeedTrap measures time only. For memory pressure use PHPUnit's --log-junit output, a memory-limit-per-test listener, or memory_get_peak_usage() assertions — this package won't help there.
  • Output can be swallowed. --no-output, some CI log formatters, and custom test printers can hide the listener's block. If you suspect this, run the single slow test directly with vendor/bin/phpunit --filter ... and watch stdout.
  • Extension point: the listener is a normal class (JohnKary\PHPUnit\Listener\SpeedTrapListener) — you can subclass it to change the report header/format or to pipe offenders to a file or Slack notification, then register your subclass in phpunit.xml instead of the vendor class.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib