Plantilla de Load Test Plan
Plantilla para planear y documentar load tests: test scenarios, user journey definitions, ramp-up strategies, success criteria, monitoring setup, tool selection (k6, JMeter, Locust), result analysis y reporting con ejemplos de codigo para cada tool.
Overview
Esta plantilla define un structured load test plan. Cubre test scenarios, user journey definitions, ramp-up strategies, success criteria, monitoring setup, tool selection (k6, JMeter, Locust), result analysis y reporting. Usa esta plantilla antes de correr load tests en un new service o antes de un major release.
1. Test Objectives
1.1 Goal Definition
Objective | Question Answered
─────────────────────────────┼──────────────────────────────────────
Baseline performance | Cual es current throughput y latency?
Capacity validation | Podemos handleer expected peak traffic?
Bottleneck identification | Donde breakea el system first?
Scalability validation | Funciona horizontal scaling como expected?
Regression detection | Degrado el latest deploy el performance?
Endurance / soak | Leakea memory over 24h de load?
Spike readiness | Podemos sobrevivir un 10x traffic spike?
1.2 Scope Definition
In scope | Out of scope
──────────────────────────────────┼──────────────────────────────────
API endpoints under test | Third-party API load testing
Database read/write performance | Browser rendering performance
Cache hit/miss behavior | Network latency simulation
Connection pool sizing | Security / penetration testing
Rate limiting behavior | UI / E2E testing
2. User Journey Definitions
2.1 Journey Mapping
Journey name | Steps | % of traffic | Requests/user
──────────────────┼──────────────────────────────────────────┼──────────────┼──────────────
Browse homepage | GET / → GET /api/products | 40% | 2
View product | GET / → GET /products/{id} → GET /api/reviews | 25% | 3
Search | GET /search?q={query} → GET /api/results | 20% | 2
Add to cart | View product → POST /api/cart → GET /api/cart | 10% | 4
Checkout | Add to cart → POST /api/checkout → POST /api/payment | 5% | 6
2.2 Test Data Requirements
Data type | Source | Volume needed | Notes
──────────────────┼─────────────────────┼──────────────────┼──────────────────
Product IDs | Production snapshot | 10000 | Usa real IDs
User accounts | Test data generator | 1000 | Pre-created accounts
Search queries | Query log sample | 500 | Realistic distribution
Payment tokens | Stripe test tokens | 100 | Usa test environment
Session tokens | Auth API | Per-test | Genera durante setup
3. Load Profiles
3.1 Ramp-Up Strategies
Profile type | Pattern | Use case
────────────────┼──────────────────────────────────┼──────────────────────────
Linear ramp | +10 users/sec until target | Baseline, capacity test
Step load | 50 → 100 → 200 → 500 (hold 5m) | Bottleneck identification
Spike | 0 → 1000 in 10s, hold 2m | Spike readiness, autoscaling
Soak | 200 users for 24h | Memory leaks, resource exhaustion
Stress | Increase until system fails | Find breaking point
3.2 Load Profile Configuration
Test phase | Duration | Users | Ramp rate | Purpose
────────────────┼──────────┼───────┼───────────┼──────────────────────
Warmup | 2 min | 10 | 5/sec | JIT compilation, caches
Baseline | 10 min | 50 | 5/sec | Normal load baseline
Ramp-up | 5 min | 200 | 30/sec | Peak traffic simulation
Peak hold | 15 min | 200 | - | Sustained peak performance
Ramp-down | 2 min | 0 | -/sec | Graceful shutdown
Cooldown | 2 min | 0 | - | Observe recovery
4. Success Criteria
4.1 Performance Thresholds
Metric | Target | Hard limit | Failure action
──────────────────────────┼─────────────┼──────────────┼──────────────────
P50 response time | < 100ms | < 200ms | Investiga
P95 response time | < 300ms | < 500ms | Investiga
P99 response time | < 500ms | < 1000ms | Investiga
Error rate | < 0.1% | < 1% | Para el test
Throughput (req/sec) | > 500 | > 300 | Investiga
CPU usage | < 70% | < 90% | Scalea up
Memory usage | < 75% | < 90% | Investiga leaks
DB connection pool usage | < 60% | < 80% | Increase pool
4.2 Pass/Fail Criteria
Result | Conditions
─────────┼──────────────────────────────────────────────────────────
PASS | All metrics within target thresholds
PASS | P95 within hard limit, P99 slightly over (documented)
FAIL | Any metric exceeds hard limit
FAIL | Error rate > 1% en any point durante el test
FAIL | System crashea o becomes unresponsive
INCONCL. | Test infrastructure issues preventieron valid results
5. Tool Selection
5.1 Tool Comparison
Tool | Language | Protocol support | Distributed | Scripting | Best for
────────┼────────────┼─────────────────────────┼─────────────┼───────────┼──────────────
k6 | JavaScript | HTTP, gRPC, WebSocket | Yes (k6 cloud) | JS/ES6 | API testing, CI/CD
JMeter | Java | HTTP, JDBC, JMS, SMTP | Yes (master/slave) | XML/Groovy | Enterprise, protocol variety
Locust | Python | HTTP, custom | Yes (master/worker) | Python | Custom protocols, flexibility
Artillery | Node.js | HTTP, WebSocket, Socket.io | Yes (AWS) | YAML/JS | Quick API tests
Gatling | Scala | HTTP, WebSocket | Yes | Scala DSL | High throughput, detailed reports
5.2 k6 Script Example
import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend } from 'k6/metrics';
const errorRate = new Rate('errors');
const responseTime = new Trend('response_time');
export const options = {
stages: [
{ duration: '2m', target: 10 },
{ duration: '10m', target: 50 },
{ duration: '5m', target: 200 },
{ duration: '15m', target: 200 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<500'],
http_req_failed: ['rate<0.01'],
errors: ['rate<0.001'],
},
};
export default function () {
group('Browse homepage', function () {
const homeRes = http.get('https://staging.example.com/');
check(homeRes, {
'homepage status 200': (r) => r.status === 200,
'homepage LCP < 2.5s': (r) => r.timings.waiting < 2500,
});
errorRate.add(homeRes.status !== 200);
responseTime.add(homeRes.timings.duration);
sleep(1);
});
group('View product', function () {
const productId = Math.floor(Math.random() * 10000) + 1;
const productRes = http.get(`https://staging.example.com/api/products/${productId}`);
check(productRes, {
'product status 200': (r) => r.status === 200,
'product has name': (r) => r.json('name') !== undefined,
});
errorRate.add(productRes.status !== 200);
responseTime.add(productRes.timings.duration);
sleep(2);
});
group('Search', function () {
const queries = ['laptop', 'phone', 'headphones', 'keyboard', 'mouse'];
const query = queries[Math.floor(Math.random() * queries.length)];
const searchRes = http.get(`https://staging.example.com/api/search?q=${query}`);
check(searchRes, {
'search status 200': (r) => r.status === 200,
'search returns results': (r) => r.json('results').length > 0,
});
errorRate.add(searchRes.status !== 200);
responseTime.add(searchRes.timings.duration);
sleep(1.5);
});
}
5.3 JMeter Test Plan (XML)
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Load Test Plan">
<stringProp name="TestPlan.comments">Peak load test — 200 users</stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Users">
<intProp name="ThreadGroup.num_threads">200</intProp>
<intProp name="ThreadGroup.ramp_time">300</intProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">1800</stringProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy testname="Homepage">
<stringProp name="HTTPSampler.domain">staging.example.com</stringProp>
<stringProp name="HTTPSampler.path">/</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion testname="Status 200">
<collectionProp name="Asserion.test_strings">
<stringProp>200</stringProp>
</collectionProp>
</ResponseAssertion>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
5.4 Locust Script (Python)
from locust import HttpUser, task, between, events
class WebsiteUser(HttpUser):
wait_time = between(1, 3)
host = "https://staging.example.com"
@task(40)
def browse_homepage(self):
with self.client.get("/", catch_response=True) as response:
if response.status_code != 200:
response.failure(f"Homepage failed: {response.status_code}")
@task(25)
def view_product(self):
product_id = random.randint(1, 10000)
with self.client.get(f"/api/products/{product_id}", catch_response=True) as response:
if response.status_code != 200:
response.failure(f"Product failed: {response.status_code}")
elif response.elapsed.total_seconds() > 0.5:
response.failure("Product too slow")
@task(20)
def search(self):
queries = ['laptop', 'phone', 'headphones', 'keyboard', 'mouse']
query = random.choice(queries)
with self.client.get(f"/api/search?q={query}", catch_response=True) as response:
if response.status_code != 200:
response.failure(f"Search failed: {response.status_code}")
@task(10)
def add_to_cart(self):
product_id = random.randint(1, 10000)
with self.client.post("/api/cart",
json={"product_id": product_id, "quantity": 1},
catch_response=True
) as response:
if response.status_code != 201:
response.failure(f"Cart failed: {response.status_code}")
@task(5)
def checkout(self):
with self.client.post("/api/checkout",
json={"payment_token": "test_token", "address_id": 1},
catch_response=True
) as response:
if response.status_code not in [200, 201]:
response.failure(f"Checkout failed: {response.status_code}")
6. Monitoring Setup
6.1 Metrics para Collectar
Layer | Metric | Tool
─────────────┼───────────────────────────┼──────────────────────────
Application | Response time percentiles | k6/JMeter/Locust built-in
Application | Error rate | k6/JMeter/Locust built-in
Application | Throughput (req/sec) | k6/JMeter/Locust built-in
Server | CPU usage | Prometheus + node_exporter
Server | Memory usage | Prometheus + node_exporter
Server | Disk I/O | Prometheus + node_exporter
Database | Active connections | pg_stat_activity / SHOW PROCESSLIST
Database | Query latency | pg_stat_statements / slow query log
Database | Lock waits | pg_locks / information_schema
Cache | Hit rate | Redis INFO / Memcached stats
Cache | Evictions | Redis INFO / Memcached stats
Network | Bandwidth | iftop / CloudWatch
6.2 Grafana Dashboard Panels
Panel name | Query (Prometheus)
────────────────────────┼──────────────────────────────────────────
Request rate | rate(http_requests_total[1m])
P95 latency | histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
Error rate | rate(http_requests_total{status=~"5.."}[1m]) / rate(http_requests_total[1m])
CPU usage | 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[1m])) * 100)
Memory usage | node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes
DB connections | pg_stat_activity_count
Cache hit rate | redis_keyspace_hits_total / (redis_keyspace_hits_total + redis_keyspace_misses_total)
7. Result Analysis and Reporting
7.1 Report Template
## Load Test Report — {Service Name}
**Date:** 2026-07-04
**Tester:** {Name}
**Environment:** Staging (replica of production)
**Tool:** k6 0.50.0
### Test Configuration
| Parameter | Value |
|-----------------|----------------|
| Max users | 200 |
| Duration | 34 min |
| Ramp-up | 5 min to 200 |
| Peak hold | 15 min at 200 |
### Results Summary
| Metric | Target | Actual | Status |
|---------------------|---------|---------|--------|
| P50 response time | < 100ms | 45ms | PASS |
| P95 response time | < 300ms | 180ms | PASS |
| P99 response time | < 500ms | 420ms | PASS |
| Error rate | < 0.1% | 0.02% | PASS |
| Throughput | > 500 | 680 rps | PASS |
| Peak CPU | < 70% | 65% | PASS |
| Peak memory | < 75% | 68% | PASS |
### Bottlenecks Identified
1. Database connection pool saturated at 180 users (pool size: 20)
- Fix: Increase pool size to 40 or add PgBouncer
2. Search API P95 spikes at 200 users (180ms → 350ms)
- Fix: Add Elasticsearch for search instead of PostgreSQL ILIKE
3. Redis eviction rate increases at peak (0 → 15/sec)
- Fix: Increase Redis maxmemory or add cache warming
### Recommendations
1. Increase DB connection pool to 40 before next traffic event
2. Migrate search to Elasticsearch (tracked in JIRA-1234)
3. Add autoscaling rule: scale at 70% CPU, not 80%
4. Re-run load test after fixes to verify improvements
See Also
Preguntas frecuentes
¿Cuál es la diferencia entre load testing y stress testing?
Load testing valida que el system handlee expected production traffic within performance targets. Simulas realistic user loads (e.g., 200 concurrent users) y verifyeas que response times, error rates y resource usage se queden within thresholds. Stress testing pushea el system mas alla de expected capacity para find su breaking point — sigues increaseando load hasta que errors spiken o el system crashea. Load testing answer "podemos handleer nuestro expected traffic?" Stress testing answer "que pasa cuando lo exceedemos?" Corre load tests antes de every major release. Corre stress tests quarterly o antes de major traffic events (Black Friday, product launches) para entender tu system's limits y autoscaling behavior.
¿Cuántos concurrent users deberia testear?
Empieza con tu current peak traffic — checkea analytics para el highest concurrent user count en los last 30 days. Testea a 1x (current peak), 2x (expected growth) y 5x (stress scenario). Si no tienes analytics data, estima desde request volume: si gets 100,000 requests per hour, eso es roughly 28 req/sec, que translates a about 50-100 concurrent users dependiendo del journey length. Para un new service sin traffic data, empieza con 100 users y scalea up. Siempre testea en un environment que mirror production — testear en un scaled-down staging environment da misleading results.
¿Qué load testing tool deberia elegir?
Para API testing con CI/CD integration, k6 es el best choice — scriptea en JavaScript, tiene built-in thresholds para pass/fail y integra con GitHub Actions. Para enterprise environments con diverse protocols (JMS, SMTP, JDBC), JMeter tiene el broadest protocol support y un GUI para non-developers. Para custom protocols o cuando needeas full programming flexibility, Locust (Python) es ideal. Para quick HTTP API tests, Artillery (Node.js) tiene el simplest YAML-based configuration. Considera tu team's language expertise — un Python team estara mas comodo con Locust, un JavaScript team con k6. All four tools soportan distributed testing para high-load scenarios.
¿Cómo simulo realistic user behavior?
Mappea real user journeys desde tu analytics — que porcentaje de users browsean, searchean, view products, checkeout? Asigna weights a cada journey en tu test script (k6: @task(40), Locust: @task(40)). Addea think time entre requests (1-5 seconds) — real users no sendean requests back-to-back. Usa realistic test data — real product IDs, real search queries desde tu query log, no random strings. Varia el data per request para que caches no skween results. Incluye authentication — most production traffic es authenticated. Testea el full journey, no solo individual endpoints — el system behavior bajo un sequence de requests difiere de isolated calls. Si tienes session management, testea session creation y expiry.
¿Qué deberia hacer si el load test failea?
Primero, determina cual threshold se excedio. Si error rate es high, checkea server logs para exceptions y database logs para connection issues. Si response times son high, checkea CPU y memory usage durante el test — si CPU hitteo 90%, el server esta under-provisioned. Si database connections estan exhausted, increasea el pool size. Si el system crasheo, checkea para memory leaks (corre un soak test) o resource exhaustion. Documenta el failure en el test report con el specific metric, el actual value y el root cause. Fixea el issue y re-corre el test. No lowerees thresholds para hacer un faileando test pasar — eso defeat el purpose. Si el failure es due a test infrastructure (no el application), markea el result como inconclusive y re-corre con fixed infrastructure.
Recursos Relacionados
Plantilla de Performance Budget
Plantilla para definir y enforcear web performance budgets: LCP, INP, CLS targets, resource budgets para JS, CSS, imagenes, fuentes, third-party scripts, CI/CD integration con Lighthouse CI y alerting thresholds con ejemplos para Next.js, Astro y SPA architectures.
DocChecklist de Auditoria de Core Web Vitals
Checklist para auditar Core Web Vitals por pagina: LCP optimization steps, INP interaction tuning, CLS layout stability fixes, field data vs lab data analysis, CrUX integration y remediation tracking con ejemplos de codigo para imagenes, fuentes, JavaScript y CSS.
DocChecklist de Database Query Tuning
Checklist para optimizacion sistematica de SQL queries: EXPLAIN plan analysis, index strategy, N+1 query detection, join optimization, pagination patterns, connection pooling, query caching y slow query log triage con ejemplos para PostgreSQL y MySQL.