TL;DR
- Load testing: Measuring system performance under expected traffic
- Goal: Verify application handles normal and peak user loads
- Key metrics: Response time (p95), throughput, error rate
- Popular tools: k6 (modern), JMeter (GUI), Gatling (Scala)
- Best practice: Test in production-like environments with realistic data
- When to run: Before releases, after major changes, regularly in CI/CD
Reading time: 12 minutes
Load testing is the practice of measuring how a system performs under expected and peak user traffic by simulating concurrent users and verifying the application maintains acceptable response times without degradation. Performance problems are measurably expensive: Google’s Core Web Vitals research found that a 100ms increase in page load time reduces conversion rates by up to 7%, and sites loading in under 1 second convert up to 3x better than those taking 5 seconds. Akamai’s web performance data confirms that 40% of users abandon a page that takes more than 3 seconds to load. Without load testing, teams discover breaking points in production — during product launches or peak seasons — when fixing them costs reputation, revenue, and engineering hours. According to ISTQB, load testing is a distinct performance test type focused on normal and anticipated traffic levels, separate from stress testing (finding failure limits) and spike testing (sudden surges). Every team with SLAs or user-facing products needs load testing before release.
What is Load Testing?
Load testing measures how a system performs under expected user traffic. It simulates many concurrent users making requests to verify the application handles the load without slowing down or failing.
Normal traffic: 100 users/minute → Response time: 200ms ✓
Peak traffic: 500 users/minute → Response time: 350ms ✓
Over capacity: 1000 users/minute → Response time: 5000ms ✗
Load testing finds the answer to: “Can our system handle the expected traffic?”
Why Load Testing Matters
“Load testing is the test you skip until you regret it. I’ve seen teams lose entire product launches because their infrastructure buckled under real traffic. A two-hour load test before release can prevent two weeks of incident response after it.” — Yuri Kan, Senior QA Lead
1. Performance Problems Are Expensive
Slow applications cost money:
| Response Time | Impact |
|---|---|
| Under 1 second | Users stay engaged |
| 1-3 seconds | 40% abandon |
| 3+ seconds | 70%+ abandon |
| 10+ seconds | Users rarely return |
Every 100ms of latency can reduce conversion rates by 1%.
2. Production Failures Are Worse
Finding problems in production means:
- Lost revenue during outages
- Damaged reputation
- Emergency fixes under pressure
- Potential data loss
Load testing finds these issues before users do.
3. Traffic Is Unpredictable
Real traffic patterns vary:
Monday 9am: Spike from work hours
Friday 5pm: Drop as users leave work
Black Friday: 10x normal traffic
Viral moment: 100x normal traffic (if you're lucky)
Load testing prepares you for peaks.
Types of Performance Testing
Load Testing
Tests expected traffic levels:
// k6 load test - expected traffic
export const options = {
stages: [
{ duration: '5m', target: 100 }, // Ramp up
{ duration: '10m', target: 100 }, // Steady state
{ duration: '5m', target: 0 }, // Ramp down
],
};
Goal: Verify normal operation.
Stress Testing
Pushes beyond expected limits:
// k6 stress test - find breaking point
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 200 },
{ duration: '5m', target: 400 },
{ duration: '5m', target: 800 }, // Beyond normal
{ duration: '2m', target: 0 },
],
};
Goal: Find maximum capacity and breaking points.
Spike Testing
Tests sudden traffic surges:
// k6 spike test - sudden surge
export const options = {
stages: [
{ duration: '1m', target: 50 },
{ duration: '30s', target: 500 }, // Sudden spike
{ duration: '2m', target: 500 },
{ duration: '30s', target: 50 }, // Drop back
],
};
Goal: Verify system handles sudden changes.
Soak Testing
Tests performance over extended periods:
// k6 soak test - extended duration
export const options = {
stages: [
{ duration: '5m', target: 100 },
{ duration: '8h', target: 100 }, // 8 hours
{ duration: '5m', target: 0 },
],
};
Goal: Find memory leaks and degradation over time.
Key Metrics to Track
Response Time
How long requests take to complete:
p50 (median): 200ms - Half of requests faster
p95: 500ms - 95% of requests faster
p99: 1200ms - 99% of requests faster
Max: 5000ms - Slowest request
Focus on p95/p99 — these show worst-case user experience.
Throughput
Requests processed per unit time:
Requests/second: 150 req/s
Transactions/second: 30 tx/s
Data transfer: 15 MB/s
Higher throughput means more capacity.
Error Rate
Percentage of failed requests:
Successful requests: 9,850
Failed requests: 150
Error rate: 1.5% ← Too high for production
Target: Under 1% error rate, ideally under 0.1%.
Resource Usage
Server-side metrics:
CPU: 75% average, 95% peak
Memory: 4GB / 8GB (50%)
Disk I/O: 200 IOPS
Network: 500 Mbps
Watch for bottlenecks approaching limits.
Writing Your First Load Test
k6 (JavaScript)
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 50, // 50 virtual users
duration: '5m', // Run for 5 minutes
thresholds: {
http_req_duration: ['p(95)<500'], // 95% under 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
},
};
export default function () {
// Test homepage
const homeResponse = http.get('https://example.com/');
check(homeResponse, {
'homepage status is 200': (r) => r.status === 200,
'homepage loads fast': (r) => r.timings.duration < 500,
});
// Simulate user think time
sleep(1);
// Test API endpoint
const apiResponse = http.get('https://example.com/api/products');
check(apiResponse, {
'api status is 200': (r) => r.status === 200,
'api returns data': (r) => JSON.parse(r.body).length > 0,
});
sleep(2);
}
Run with: k6 run load-test.js
JMeter
JMeter uses XML configuration, typically created via GUI:
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan>
<hashTree>
<ThreadGroup>
<stringProp name="ThreadGroup.num_threads">50</stringProp>
<stringProp name="ThreadGroup.ramp_time">30</stringProp>
<stringProp name="ThreadGroup.duration">300</stringProp>
<HTTPSamplerProxy>
<stringProp name="HTTPSampler.domain">example.com</stringProp>
<stringProp name="HTTPSampler.path">/api/products</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
</ThreadGroup>
</hashTree>
</jmeterTestPlan>
Run with: jmeter -n -t test.jmx -l results.jtl
Locust (Python)
# locustfile.py
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 3) # Think time
@task(3) # Higher weight
def view_homepage(self):
self.client.get("/")
@task(2)
def view_products(self):
self.client.get("/api/products")
@task(1)
def view_product_detail(self):
self.client.get("/api/products/1")
Run with: locust -f locustfile.py --host=https://example.com
Load Testing Best Practices
1. Test in Production-Like Environments
Environment matters:
❌ Testing on localhost with 1GB database
✓ Testing on staging with production-size data
Localhost results don't predict production performance
Match:
- Server specifications
- Database size
- Network configuration
- Third-party integrations
2. Use Realistic Test Data
// Bad: Always same request
http.get('/api/users/1');
// Good: Varied, realistic requests
const userId = Math.floor(Math.random() * 10000) + 1;
http.get(`/api/users/${userId}`);
Vary parameters to hit different code paths and cache behaviors.
3. Simulate Real User Behavior
Users don’t click as fast as scripts:
export default function () {
http.get('/');
sleep(2); // Think time: reading homepage
http.get('/products');
sleep(3); // Think time: browsing products
http.post('/cart', { productId: 123 });
sleep(1); // Quick action
}
Include realistic delays between actions.
4. Define Clear Pass/Fail Criteria
Set thresholds before testing:
export const options = {
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
http_reqs: ['rate>100'], // Minimum throughput
},
};
Tests should pass or fail automatically.
5. Test Regularly
Load testing shouldn’t be a one-time event:
# GitHub Actions - weekly load test
on:
schedule:
- cron: '0 2 * * 0' # Every Sunday at 2am
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: k6 run tests/load-test.js
Catch regressions before they reach production.
Load Testing Tools Comparison
| Tool | Language | Best For | Learning Curve |
|---|---|---|---|
| k6 | JavaScript | Developers, CI/CD | Low |
| JMeter | Java/XML | QA teams, GUI users | Medium |
| Gatling | Scala | High performance | Medium |
| Locust | Python | Python teams | Low |
| Artillery | JavaScript | Serverless apps | Low |
Choosing a Tool
Choose k6 if:
- Developers write tests
- Need CI/CD integration
- Prefer code over GUI
Choose JMeter if:
- QA team writes tests
- Need visual test design
- Extensive plugin ecosystem needed
Choose Gatling if:
- Need high performance
- Scala/JVM environment
- Detailed HTML reports
Interpreting Results
Healthy Results
✓ http_req_duration..........: avg=180ms p95=320ms p99=450ms
✓ http_req_failed............: 0.02%
✓ http_reqs..................: 15000 (250/s)
✓ vus........................: 50
Low response times, minimal errors, consistent throughput.
Problem Indicators
✗ http_req_duration..........: avg=2500ms p95=8000ms p99=15000ms
✗ http_req_failed............: 15%
http_reqs..................: 3000 (50/s)
vus........................: 50
Red flags:
- High response times (especially p95/p99)
- Error rate above 1%
- Throughput drops under load
- High resource usage
Finding Bottlenecks
When tests fail, investigate:
- Application code — Slow queries, inefficient algorithms
- Database — Query optimization, indexing, connection pooling
- Infrastructure — CPU/memory limits, network bandwidth
- Third parties — Slow external APIs, rate limits
// Add detailed logging to identify bottlenecks
const response = http.get('/api/users');
console.log(`DNS: ${response.timings.blocked}ms`);
console.log(`Connect: ${response.timings.connecting}ms`);
console.log(`TLS: ${response.timings.tls_handshaking}ms`);
console.log(`Waiting: ${response.timings.waiting}ms`);
console.log(`Receiving: ${response.timings.receiving}ms`);
FAQ
What is load testing?
Load testing measures how a system performs under expected user traffic. It simulates many concurrent users making requests simultaneously to verify the application handles normal and peak loads without performance degradation. Unlike functional testing that checks if features work, load testing checks if features work under realistic traffic conditions. The goal is to answer: “Can our system handle the expected number of users?”
What’s the difference between load testing and stress testing?
Load testing simulates expected traffic levels to verify normal operation. Stress testing intentionally exceeds expected limits to find breaking points. Think of load testing as “can we handle normal traffic?” and stress testing as “when do we break?” Load testing uses realistic user numbers; stress testing pushes until the system fails. Both are valuable — load testing for validation, stress testing for capacity planning.
What metrics should I track in load testing?
Track these key metrics: Response time percentiles (p50, p95, p99) show how users experience performance — p95 means 95% of requests are faster than this value. Throughput (requests per second) shows capacity. Error rate (percentage of failed requests) should stay under 1%. Resource usage (CPU, memory, disk I/O) reveals bottlenecks. Focus on p95 response time as your primary metric — it represents real user experience better than averages.
What tools are used for load testing?
Popular load testing tools include k6 (JavaScript-based, modern, developer-friendly), JMeter (Java, GUI-based, extensive plugins), Gatling (Scala, high performance), and Locust (Python-based, easy to learn). k6 is ideal for developers who want tests as code in CI/CD. JMeter suits QA teams preferring visual test design. Choose based on your team’s skills and integration needs.
Further Reading and Sources
- ISTQB Glossary: Performance Testing — Formal definitions for load, stress, and spike testing
- k6 Documentation — Official k6 load testing reference and best practices
See Also
- k6 vs JMeter - Load testing tool comparison
- JMeter vs Gatling - Performance testing tools
- JMeter Tutorial - Getting started with JMeter
- Performance Testing Guide - Best practices
