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

Pest Plugin Stressless Laravel Package

pestphp/pest-plugin-stressless

A Pest plugin for finding flaky tests. Stressless runs your suite repeatedly to surface intermittent failures, reporting tests that pass and fail unpredictably so you can fix nondeterminism before it reaches CI.

View on GitHub
Deep Wiki
Context7

Getting Started

Add the plugin as a dev-only dependency and let Pest's plugin discovery pick it up — there is nothing to register by hand:

composer require pestphp/pest-plugin-stressless:^2.2 --dev

Pin the minor. The last tagged release is December 2023, the project is community-maintained with no published compatibility matrix, and a Pest major upgrade is the most likely breaking event. ^2.2 keeps you on the version that added all-HTTP-verb support, which is the release that makes the package useful for anything other than read paths.

Where to look first. In this order:

  1. vendor/pestphp/pest-plugin-stressless/README.md — the actual contract. The public surface is small and the README is the only reliable spec.
  2. vendor/pestphp/pest-plugin-stressless/src/ — confirm the helper namespace (typically Pest\Stressless\stress) and the shape of the result object before you write assertions.
  3. tests/Pest.php — where you bind the stress suite to its own directory and group.

First use case: a discovery test, not an assertion. The plugin fire real HTTP requests at a running application, so step zero is confirming you have a target that handles concurrency (see Gotchas — artisan serve does not). Then write one test whose only job is to show you the result object:

use function Pest\Stressless\stress;

it('shows me what a stress run returns', function () {
    $result = stress(rtrim(env('STRESS_BASE_URL', 'http://127.0.0.1:8000'), '/') . '/up')
        ->run(requests: 10, concurrency: 1);

    dump($result); // read the real method names and metric fields here
});

Run it with ./vendor/bin/pest --filter="shows me". Once you can see throughput, per-request durations, and the success/failure counts, convert the dump into assertions. Do this before writing a single threshold — the assertion helpers are the least documented part of the package, and guessing at them wastes more time than one discovery run.

Implementation Patterns

Isolate the suite from day one. Stress tests are slow and CPU-hungry; if they run on every save the team will disable them within a week. Two changes, both required:

// tests/Pest.php
uses()->group('stress')->in('Stress');
<!-- phpunit.xml -->
<groups>
    <exclude>
        <group>stress</group>
    </exclude>
</groups>

Then keep the default script clean (./vendor/bin/pest skips them automatically) and add an explicit entry point for the load job:

./vendor/bin/pest --group=stress

Target through an environment variable, never a literal. STRESS_BASE_URL should point at a dedicated, resettable, non-production environment. With all verbs available since v2.2.0, a stray run against shared staging can mutate real fixtures or take the environment down for whoever else is using it.

Authenticate once, reuse the token. HTTP stress requests bypass Sanctum::actingAs() entirely — they hit a separate process with a real connection. Create the user and token in a setup step and attach the header to every virtual request:

beforeEach(function () {
    $this->user  = User::factory()->create();
    $this->token = $this->user->createToken('stress')->plainTextToken;
    $this->url   = config('stress.base_url') . '/api/orders';
});

For write paths, make every request's payload unique. Concurrency plus a shared unique constraint produces duplicate-key failures that look like application bugs but are test artefacts. Pre-generate a pool of distinct payloads (or a counter appended to the identifier) sized to the request count.

Warm up, then measure. Run a small unscored pass first — it fills opcache, primes config/route caches, and establishes the connection pool — then run the measured pass. Comparing a cold first run to a warm baseline is the fastest way to generate a fake regression.

Ladder your assertions. In order of reliability:

  1. failed() === 0 — the strongest and least flaky signal. A successful stress run that returns errors is a real finding.
  2. A throughput floor with generous headroom (toBeGreaterThan($baseline * 0.6)), stored in a committed baseline file rather than a magic number.
  3. p95/p99 duration compared relatively to the last recorded baseline.

Never start with an absolute millisecond threshold. Runner CPU contention, noisy neighbours, and cache state move absolute numbers by 2–3× between identical runs.

Use the repeat loop for a different job. The plugin also supports repeating the suite to surface order-dependent and flaky tests. That is a correctness tool, not a load tool — conflating the two produces numbers nobody should act on. Keep flaky detection on the normal suite, and load measurement on the HTTP scenarios.

In CI, run it as its own job against its own environment, started as a service (compose, Octane, or FrankenPHP), not on the same box as the test runner. Publish the metrics as a job artifact so a regression has evidence attached, and only promote a scenario to a gating check once you've seen the run-to-run variance over at least a week.

Gotchas and Tips

php artisan serve serialises everything. The built-in server handles one request at a time, so every concurrency number you collect is fiction. Use Octane, FrankenPHP, or an nginx+FPM container; if you must use php -S, set PHP_CLI_SERVER_WORKERS. Confirm the target actually parallelises with a two-request sanity check before trusting any metric.

The runner and the app compete. On a single CI runner, the load generator saturates its own CPU and network before the application does. Treat the numbers as a lower bound on the app's ceiling — useful for catching regressions, useless for capacity planning. Say so explicitly in the test's docblock so nobody signs off on infrastructure sizing from it.

RefreshDatabase and DatabaseTransactions do not apply. Those wrap the test process's connection in a transaction; the requests under stress open their own connections in a different process and commit for real. Use a dedicated database for the stress environment and truncate/reset between runs as a fixture step.

Validate the method string in review. v2.2.0's permissive verb handling means a typo can silently issue an unintended request against a mutating endpoint. Keep the verb a literal in the test body, not interpolated from config or a loop variable.

Debug in this order: one request at a time (requests: 1, concurrency: 1) → inspect the status code and body of the failure → only then scale up. Nearly every "the plugin is broken" report resolves to a 404, a CSRF rejection, or a redirect at concurrency 1.

Watch the Pest major upgrade. There is no compatibility matrix and no vendor SLA. Before bumping Pest, run one known stress scenario, diff the metrics against the committed baseline, and be ready to vendor or fork if an upstream issue goes unanswered. Keep the scenarios small and self-contained so a fork stays cheap.

Extension ceiling. Headers, auth, and payload shape cover most real scenarios. Anything beyond that — multi-step journeys with think-time, correlation, non-HTTP protocols — is out of scope, and the honest answer is k6 or Locust, not a patch to this plugin. Establish that boundary with the team early so the tool isn't blamed for questions it never claimed to answer.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings