// docs / stress-testing

Stress Testing

Stress testing is a type of testing that inspects the stability and reliability of your application under realistic or extreme conditions — depending on the scenario you set up. For example, you may use stress testing to verify that your application can handle a large number of requests, or a large amount of data.

In Pest, you may combine the power of stress testing with the Expectation API, ensuring no stability and reliability regressions over time. This is helpful when you want to verify that your application remains stable and reliable after a new release, or after a new deployment.

Behind the scenes, this plugin utilizes k6, a powerful open-source load testing tool for evaluating the performance of APIs, microservices, and websites. k6 is licensed under the AGPL-3.0 License, and the k6 binary is downloaded automatically when the plugin is used for the first time.

To get started with Pest's Stress Testing plugin (also known as Stressless), you may require the plugin via Composer:

1composer require pestphp/pest-plugin-stressless --dev

Once the plugin is installed, you may start using it in two different ways:

  • Using the stress command: helpful when you want to quickly stress test a URL, without setting expectations on the result.
  • Using the stress() function: helpful when you want to stress test a URL and set expectations on the result.

Testing an external domain or a local IP address? When load testing a domain from an external network, you get a realistic picture of how your application performs under typical user loads. This includes factors like network latency and real-world internet traffic. However, when testing a local IP address within your network, the focus shifts to the performance of your internal infrastructure in a controlled environment, without external variables like internet or DNS resolution times. This is particularly helpful for identifying potential bottlenecks within your own network or server, and for performance tuning of internal applications or servers — tasks such as configuring PHP FPM more effectively, for example.

The Stress Command

The stress command is helpful when you want to quickly stress test a URL and analyze the result, all without setting expectations on the result. It's the quickest way to launch a stress test, and it happens directly in the terminal.

To get started, you may use the stress command and provide the URL you wish to stress test:

1./vendor/bin/pest stress example.com

By default, the stress test duration will be 5 seconds. However, you may customize this value using the --duration option:

1./vendor/bin/pest stress example.com --duration=5

In addition, the number of concurrent requests will be 1 by default. However, you may also customize this value using the --concurrency option:

1./vendor/bin/pest stress example.com --concurrency=5

The concurrency value represents the number of concurrent requests that will be made to the given URL. For example, if you set the concurrency to 5, Pest will constantly make 5 concurrent requests to the given URL until the stress test duration is reached.

You may wish to be mindful of the number of concurrent requests you configure. If you configure too many concurrent requests, you may overwhelm your application or server, or hit rate limits and firewalls.

Sometimes you may wish to specify the HTTP method used for the stress test. To accomplish this, you may use one of the provided delete, get, head, options, patch, put, or post options. With the options, patch, and put options, you may specify an optional payload argument to be used in the requests. With the post option, you are required to provide the payload argument:

1./vendor/bin/pest stress example.com/articles
2# or
3./vendor/bin/pest stress example.com/articles --get
4# or
5./vendor/bin/pest stress example.com/articles --head
6# or
7./vendor/bin/pest stress example.com/articles --options
8# or
9./vendor/bin/pest stress example.com/articles --options='{"name": "Nuno"}'
10# or
11./vendor/bin/pest stress example.com/articles/1 --patch
12# or
13./vendor/bin/pest stress example.com/articles/1 --patch='{"name": "Nuno"}'
14# or
15./vendor/bin/pest stress example.com/articles --put
16# or
17./vendor/bin/pest stress example.com/articles --put='{"name": "Nuno"}'
18# or
19./vendor/bin/pest stress example.com/articles --post='{"name": "Nuno"}'
20# or
21./vendor/bin/pest stress example.com/articles/1 --delete

Once the stress test is completed, Pest will display a summary of the stress test result.

The Stress Function

Once you understand how stress testing works, you may wish to start setting expectations on the stress test result. For example, you may want to verify that the average response time is always less than 100ms, and this is where the stress() function comes in.

To get started, create a regular test and use the stress() function to stress test a given URL:

1<?php
2 
3use function Pest\Stressless\stress;
4 
5it('has a fast response time', function () {
6 $result = stress('example.com');
7 
8 expect($result->requests()->duration()->med())->toBeLessThan(100); // < 100.00ms
9});

By default, the stress test duration will be 10 seconds. However, you may customize this value using the for()->seconds() method:

1$result = stress('example.com')->for(5)->seconds();

In addition, the number of concurrent requests will be 1 by default. However, you may also customize this value using the concurrently method:

1$result = stress('example.com')->concurrently(requests: 2)->for(5)->seconds();

At any time, you may dd the stress test result to see its details, as you would when using the stress command:

1$result = stress('example.com')->dd();
2 //->dump();
3 //->verbosely();

Sometimes you may wish to specify the HTTP method used for the stress test. To accomplish this, you may use one of the provided delete, get, head, options, patch, put, or post methods. With the options, patch, and put methods, you may specify an optional payload argument to be used in the requests. With the post method, you are required to provide the payload argument:

1$result = stress('example.com/articles/1')->delete();
2// or
3$result = stress('example.com/articles')->get();
4// or
5$result = stress('example.com/articles')->head();
6// or
7$result = stress('example.com/articles')->options();
8// or
9$result = stress('example.com/articles')->options(["name" => "Nuno"]);
10// or
11$result = stress('example.com/articles/1')->patch();
12// or
13$result = stress('example.com/articles/1')->patch(["name" => "Nuno"]);
14// or
15$result = stress('example.com/articles')->put();
16// or
17$result = stress('example.com/articles')->put(["name" => "Nuno"]);
18// or
19$result = stress('example.com/articles')->post(["name" => "Nuno"]);

If you wish to specify request headers, you may use the provided headers method:

1$result = stress('example.com/articles')->headers([
2 'Authorization' => 'Bearer SecretToken',
3])->get();

The stress() function returns the stress test result, which you may use to set expectations. Here is the list of available methods:

Request Duration

Returns the overall request duration in milliseconds:

1$result->requests()->duration()->med();
2 // ->min();
3 // ->max();
4 // ->p90();
5 // ->p95();

Requests Count

Returns the number of requests made:

1$result->requests()->count();

Requests Rate

Returns the number of requests made per second:

1$result->requests()->rate();

Requests Failed Count

Returns the number of requests that failed:

1$result->requests()->failed()->count();

Requests Failed Rate

Returns the number of requests that failed per second:

1$result->requests()->failed()->rate();

Requests Time To First Byte Duration / TTFB

Returns the request time to first byte duration in milliseconds:

1$result->requests()->ttfb()->duration()->med();
2 // ->min();
3 // ->max();
4 // ->p90();
5 // ->p95();

Requests DNS Lookup Duration

Note: This metric is affected by the network latency between the client and the DNS server.

Returns the request DNS lookup duration in milliseconds:

1$result->requests()->dnsLookup()->duration()->med();
2 // ->min();
3 // ->max();
4 // ->p90();
5 // ->p95();

Requests TLS Handshaking Duration

Note: This metric is affected by the network latency between the client and the server.

Returns the request TLS handshaking duration in milliseconds:

1$result->requests()->tlsHandshaking()->duration()->med();
2 // ->min();
3 // ->max();
4 // ->p90();
5 // ->p95();

Requests Download Duration

Note: This metric is affected by the network latency between the client and the server.

Returns the request download duration in milliseconds:

1$result->requests()->download()->duration()->med();
2 // ->min();
3 // ->max();
4 // ->p90();
5 // ->p95();

Requests Download Data Count

Returns the request download data count in bytes:

1$result->requests()->download()->data()->count();

Requests Download Data Rate

Returns the request download data rate in bytes per second:

1$result->requests()->download()->data()->rate();

Requests Upload Duration

Note: This metric is affected by the network latency between the client and the server.

Returns the request upload duration in milliseconds:

1$result->requests()->upload()->duration()->med();
2 // ->min();
3 // ->max();
4 // ->p90();
5 // ->p95();

Requests Upload Data Count

Returns the request upload data count in bytes:

1$result->requests()->upload()->data()->count();

Requests Upload Data Rate

Returns the request upload data rate in bytes per second:

1$result->requests()->upload()->data()->rate();

Test Run Concurrency

Returns the number of concurrent requests made during the stress test, which is the value you set using the --concurrency option or the concurrently method:

1$result->testRun()->concurrency();

Test Run Duration

Returns the duration of the stress test, which is the value you set using the --duration option or the for()->seconds() method:

1$result->testRun()->duration();

In this chapter, we've seen how to use Pest's Stress Testing plugin (also known as Stressless) to stress test a given URL and set expectations on the result. Next, let's explore how to evaluate the quality of LLM agents and AI-generated output directly from your test suite: Evals