Stress testing is a form of performance testing where the application is pushed beyond its normal operational capacity.
The goal is to determine the applicationβs robustness, to identify breaking points, and to ensure that the system gracefully handles or recovers from heavy loads.
developers design these tests to simulate real-world scenariosβsometimes focusing on extreme usage conditions (often in a staging environment) to ensure reliability when deployed.
Folder Structure for Stress Testing
While folder structures can vary from one team to another, developers often follow these conventions to keep their project organized:
- Separate Test Folders:
Developers usually keep stress tests separate from unit tests and integration tests. A common approach is to create a dedicated folder:
βββ test/
β βββ unit/
β βββ integration/
β βββ stress/
β βββ scenarios/
β β βββ highConcurrency.js
β β βββ longDuration.js
β βββ config/
β β βββ artillery-config.yml
β βββ helpers/
β βββ loadGenerator.js
- Scenarios Folder:
Under the stress directory, you will often find a scenarios or cases folder where each file represents a different load scenarioβthis could be a test for high concurrency, prolonged load, or burst traffic.
- Config Folder:
A folder for configuration files (e.g., YAML or JSON) is common.
Tools like Artillery or k6 use these configuration files to define the parameters of the test (such as the number of virtual users, duration, endpoints, etc.).
- Helpers Folder:
You might include helper scripts or libraries that assist in running the tests or processing the output data.
Best Practices and Considerations
1. Define Clear Objectives:
Define what βstressβ means for your application.
Determine ** key performance indicators** (KPIs) like response times, throughput, error rates, and system resource usage.
Know the normal performance metrics so you can detect anomalies when under stress. (Establish Baselines)
2. Test Environment Setup:
Always run stress tests in an environment similar to production but isolated, so that testing does not affect real users.
Use data and configurations that mirror your production environment as closely as possible to identify realistic bottlenecks.
3. Incremental Testing:
Begin with a lower load and gradually increase it to pinpoint the exact threshold where your application starts to degrade.
Use monitoring and logging tools (e.g., New Relic, Datadog) to observe system behavior during testing. This will help you understand resource usage and failure points.
4. Consider External Factors:
Account for network latencies, especially if your Node.js application communicates with external services.
Evaluate your load balancers, caching strategies, and database performance as these components also affect overall system performance.
stress test scenarios
Below is a list of common stress test scenarios that developers typically design and run.
Each scenario focuses on a different aspect of application behavior under extreme load, ensuring a comprehensive evaluation of the system.
1.High Concurrency Test
Simulate a large number of simultaneous users or connections.
Focus: Assess how the application handles many parallel requests and identify bottlenecks in processing or resource management.
config:
target: "http://localhost:3000"
phases:
- duration: 60 # Duration in seconds
arrivalRate: 100 # 100 new virtual users per second
scenarios:
- flow:
- get:
url: "/api/endpoint"
2.Sustained Load (Prolonged Load) Test
Run the application under a steady load for an extended period (e.g., several minutes to hours).
Focus: Detect memory leaks, gradual degradation in performance, and stability issues over time.
config:
target: "http://localhost:3000"
phases:
- duration: 600 # 10 minutes duration
arrivalRate: 20 # Steady rate of 20 users per second
scenarios:
- flow:
- get:
url: "/api/endpoint"
3.Burst Traffic (Spike) Test
Simulate sudden increases in traffic in short bursts.
Focus: Evaluate the applicationβs ability to handle unexpected spikes and its capability to recover when the traffic subsides.
config:
target: "http://localhost:3000"
phases:
- duration: 10 # Normal load phase
arrivalRate: 50
- duration: 5 # Burst phase
arrivalRate: 200
- duration: 10 # Post-burst cooldown
arrivalRate: 50
scenarios:
- flow:
- get:
url: "/api/endpoint"
4.Resource Exhaustion Test
Push system resources (such as CPU, memory, disk I/O) to their limits.
Focus: Identify when and how resource saturation occurs,and verify that appropriate safeguards (like rate limiting or backpressure) are in place.
config:
target: "http://localhost:3000"
phases:
- duration: 60
arrivalRate: 10
- duration: 60
arrivalRate: 50
- duration: 60
arrivalRate: 100
scenarios:
- flow:
- get:
url: "/api/endpoint"
You made it this far π. Now dig deep and finish strong!
5.Connection Flood Test
Open a very high number of connections rapidly to stress the network and server limits.
Focus: Understand how the application and its infrastructure deal with numerous open sockets and potential connection timeouts or errors.
config:
target: "http://localhost:3000"
phases:
- duration: 30
arrivalRate: 500 # Very high arrival rate to flood with connections
scenarios:
- flow:
- get:
url: "/api/endpoint"
6.Slow Request (Slowloris) Test
Send requests very slowly to keep connections open without completing them.
Focus: Determine how the server manages idle connections and whether it implements proper timeout and resource recovery mechanisms.
config:
target: "http://localhost:3000"
phases:
- duration: 60
arrivalRate: 10
scenarios:
- flow:
- function: "sendSlowRequest"
functions:
sendSlowRequest: |
function(userContext, events, done) {
const http = require('http');
const options = {
hostname: 'localhost',
port: 3000,
path: '/api/slow-endpoint',
method: 'GET'
};
const req = http.request(options, (res) => {
res.on('data', () => {});
res.on('end', () => { done(); });
});
req.on('error', (e) => { done(e); });
// Simulate slow sending of request: write partial data then wait before finishing the request
req.write('partialData');
setTimeout(() => {
req.end();
}, 10000); // Delay of 10 seconds before completing the request
}
7.Error Handling and Recovery Test
Intentionally trigger errors or service failures during stress.
Focus: Test how gracefully the application degrades, how errors are logged, and whether recovery mechanisms (like retry logic or circuit breakers) kick in effectively.
config:
target: "http://localhost:3000"
phases:
- duration: 60
arrivalRate: 20
scenarios:
- flow:
- get:
url: "/api/error" # This endpoint should simulate errors
8.Dependency Under Load Test
Simulate high load on external dependencies (such as databases, cache systems, or third-party APIs).
Focus: Determine the impact on your Node.js application when underlying services are slow or unavailable, ensuring the application handles such scenarios gracefully.
config:
target: "http://localhost:3000"
phases:
- duration: 60
arrivalRate: 50
scenarios:
- flow:
- get:
url: "/api/dependency-endpoint"
9.Simulated Distributed Denial of Service (DDoS) Scenario
Mimic a DDoS attack by overwhelming the system with a flood of requests from multiple sources.
Focus: Evaluate the effectiveness of security measures, rate limiting, and traffic filtering under an extreme flood of network requests.
config:
target: "http://localhost:3000"
phases:
- duration: 30
arrivalRate: 1000 # Extreme load
scenarios:
- flow:
- get:
url: "/api/endpoint"
Common Tools Used for Stress Testing in Node.js
Artillery
Artillery is popular for its simple YAML configuration files which define load scenarios and integrates well with Node.js applications.
k6
The scripts are written in JavaScript which makes it familiar for Node.js developers. The tests can be automated as part of your CI/CD pipeline.
autocannon
Itβs often used for quick benchmarks and stress testing, especially when you need to focus on the performance of a single endpoint or a small set of endpoints
Loadtest
The module can be executed as part of your test suite, though it might not be as robust for very large-scale testing as some other tools.
This is your boss after you read this article.π lol
other posts:
Let's connect!!: π€
Top comments (1)
The examples I used are from the Artillery tool.