Plantilla de Plan de Pruebas de Carga
Plantilla rellenable de plan de pruebas de carga: escenarios, perfiles de ramp-up, umbrales de éxito, monitoreo y scripts listos para k6, JMeter y Locust.
Overview
Las pruebas de carga fracasan mucho antes de la primera petición: cuando los objetivos son vagos, los journeys de usuario se inventan sobre la marcha y los umbrales de éxito se negocian después de ver los resultados. Esta plantilla resuelve eso: un plan estructurado que se rellena antes de ejecutar la prueba, con objetivos, journeys de usuario, perfiles de carga, criterios de éxito, selección de herramienta (k6, JMeter, Locust), monitoreo y reporting. Úsala antes de una release importante, una migración o cualquier pico de tráfico esperado — y combínala con la Plantilla de Plan de Ejecución de Pruebas de Carga cuando necesites un runbook para el día de la prueba.
Cuándo Usar Esta Plantilla
- Antes de una release importante, una migración o un cambio de infraestructura (base de datos nueva, capa de caché, reglas de escalado).
- Ante picos de tráfico esperados: Black Friday, lanzamientos de producto, campañas de marketing.
- Para validar capacidad contra los SLOs o responder “¿aguantamos el doble del pico actual?”.
- En ejecuciones trimestrales de línea base que detectan regresiones de rendimiento a tiempo.
- Cuando un servicio tiene que pasar una revisión de production readiness y necesita evidencia, no anécdotas.
No uses esta plantilla para corrección funcional (para eso están los tests de integración y de contrato), pruebas de seguridad ni rendimiento de renderizado en navegador — eso requiere otras herramientas y otro documento. Si solo necesitas generar carga contra una API sin un plan formal, ve directo a la receta de load testing.
Cómo Usar Esta Plantilla
Rellena las secciones en orden: los journeys que mapees en §2 determinan el perfil de §3, y el perfil define los umbrales de §4. Saltarse pasos es como los planes acaban con números que nadie puede defender.
- Rellena los objetivos (§1). Elige 1-3 metas principales — un plan que intenta responderlo todo no responde nada.
- Mapea los journeys reales (§2). Saca la distribución de tráfico de tus analytics o access logs; no adivines los porcentajes.
- Elige un perfil de carga (§3). Ajusta el perfil al objetivo: step load para cazar cuellos de botella, soak para fugas de memoria, spike para validar el autoscaling.
- Adapta los umbrales a tus SLOs (§4). Los valores de abajo son puntos de partida razonables, no verdades universales — un checkout y un exportador de métricas no comparten presupuesto de latencia.
- Elige herramienta y adapta el script (§5). Parte del ejemplo más cercano a tu stack; la receta de load testing con k6 cubre thresholds y aserciones con más detalle.
- Conecta el monitoreo antes de ejecutar (§6). Si durante la prueba no ves CPU, memoria y métricas de base de datos del lado del servidor, los números que recojas son anécdotas.
- Ejecuta y registra (§7). Rellena el informe durante la prueba, no días después — la plantilla de informe de pruebas de carga lo expande hasta un documento presentable a stakeholders.
- Comparte, corrige, repite. Un plan sin re-ejecución programada tras los arreglos es un documento, no una práctica.
1. Test Objectives
Antes de tocar cualquier herramienta, anota la pregunta que esta prueba tiene que responder. Cada decisión posterior — journeys, perfil de carga, umbrales — debería poder rastrearse hasta una de estas metas.
1.1 Goal Definition
Objective | Question Answered
─────────────────────────────┼──────────────────────────────────────────
Baseline performance | ¿Cuál es el throughput y latencia actual?
Capacity validation | ¿Aguantamos el pico de tráfico esperado?
Bottleneck identification | ¿Dónde rompe el sistema primero?
Scalability validation | ¿El escalado horizontal funciona?
Regression detection | ¿El último deploy degradó el rendimiento?
Endurance / soak | ¿Hay fugas de memoria en 24h de carga?
Spike readiness | ¿Sobrevivimos a un pico de tráfico 10x?
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 IDs reales
User accounts | Test data generator | 1000 | Cuentas pre-creadas
Search queries | Query log sample | 500 | Distribución realista
Payment tokens | Stripe test tokens | 100 | Usa el entorno de pruebas
Session tokens | Auth API | Per-test | Genera durante el 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 | Investigar
P95 response time | < 300ms | < 500ms | Investigar
P99 response time | < 500ms | < 1000ms | Investigar
Error rate | < 0.1% | < 1% | Detener la prueba
Throughput (req/sec) | > 500 | > 300 | Investigar
CPU usage | < 70% | < 90% | Escalar
Memory usage | < 75% | < 90% | Investigar fugas
DB connection pool usage | < 60% | < 80% | Aumentar el pool
4.2 Pass/Fail Criteria
Result | Conditions
─────────┼──────────────────────────────────────────────────────────
PASS | Todas las métricas dentro de los umbrales objetivo
PASS | P95 dentro del límite duro, P99 ligeramente por encima (documentado)
FAIL | Alguna métrica supera el límite duro
FAIL | Error rate > 1% en cualquier punto de la prueba
FAIL | El sistema crashea o deja de responder
INCONCL. | Problemas de infraestructura de pruebas invalidaron los resultados
5. Tool Selection
Elige la herramienta que encaje con el lenguaje de tu equipo y tus necesidades de protocolo — no la que tenga más estrellas en GitHub. Los tres scripts de abajo implementan los mismos journeys y umbrales de las secciones anteriores, así que puedes compararlos directamente.
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 }, // warmup
{ duration: '10m', target: 50 }, // baseline
{ duration: '5m', target: 200 }, // ramp-up
{ duration: '15m', target: 200 }, // peak hold
{ duration: '2m', target: 0 }, // ramp-down
],
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<500'],
http_req_failed: ['rate<0.01'],
errors: ['rate<0.001'],
},
};
export default function () {
// Journey: Browse homepage
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);
});
// Journey: View product
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);
});
// Journey: Search
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)
import random
from locust import HttpUser, task, between
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 to Collect
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
Una prueba de carga que nadie lee es una prueba que nunca ocurrió. El informe de abajo es deliberadamente corto — una página de configuración, resultados, cuellos de botella y recomendaciones que un stakeholder puede escanear en dos minutos. Los números sin veredicto invitan a reinterpretarlos, así que cierra siempre con recomendaciones explícitas y una re-ejecución programada.
7.1 Report Template
## Load Test Report — {Service Name}
**Date:** 2026-10-14
**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
Errores Comunes
Declarar pass/fail después de la prueba
Los umbrales negociados después de ver los resultados no son umbrales — son racionalizaciones. Escribe los criterios de éxito en el plan antes de que nadie ejecute nada, y trata el “ya sabremos qué es un buen resultado cuando lo veamos” como una bandera roja en la revisión.
Probar en un entorno reducido
Un staging a una décima parte del tamaño de producción te dice muy poco sobre la capacidad real. Si el entorno de pruebas no puede replicar producción, dilo en el plan y marca los resultados como orientativos, no concluyentes.
Replicar siempre los mismos datos de petición
Enviar el mismo product ID o la misma búsqueda en cada petición hace que las cachés absorban la carga e infla los números. Rota IDs reales, queries reales de tus logs y una distribución realista — para eso está la sección §2.2.
Sin think time entre peticiones
Los usuarios reales hacen pausas. Disparar peticiones sin descanso mide un ataque de denegación de servicio, no tráfico de usuarios, y produce números de throughput que tu sistema nunca verá. Mantén los sleeps de 1-5 segundos en los scripts.
Generador de carga en la misma máquina
Ejecutar k6 o Locust en el mismo host o segmento de red que el sistema bajo prueba oculta la latencia de red y roba CPU a lo que estás midiendo. Genera la carga desde una máquina separada — idealmente en la misma región que tus usuarios.
See Also
- Realizar Load Testing en APIs
- Testing de Carga de APIs con k6 y Aserciones Basadas en Umbrales
- Plantilla de Informe de Pruebas de Carga: Benchmarks y Cuellos de Botella
- Plantilla de Plan de Ejecución de Pruebas de Carga
- Plantilla de Pronóstico de Capacidad
- Documentación de k6
- Documentación de Locust
- Archivos complementarios — scripts ejecutables de k6/JMeter/Locust y las plantillas rellenables de plan e informe
Preguntas frecuentes
¿Cuál es la diferencia entre load testing y stress testing?
El load testing valida que el sistema aguanta el tráfico de producción esperado dentro de los objetivos de rendimiento. Simulas cargas de usuario realistas (p. ej., 200 usuarios concurrentes) y verificas que los tiempos de respuesta, las tasas de error y el uso de recursos se mantienen dentro de los umbrales. El stress testing empuja el sistema más allá de la capacidad esperada para encontrar su punto de ruptura — sigues aumentando la carga hasta que los errores se disparan o el sistema cae. El load testing responde "¿podemos con el tráfico esperado?". El stress testing responde "¿qué pasa cuando lo superamos?". Ejecuta load tests antes de cada release importante. Ejecuta stress tests trimestralmente o antes de grandes eventos de tráfico (Black Friday, lanzamientos) para conocer los límites del sistema y cómo se comporta el autoscaling.
¿Con cuántos usuarios concurrentes debería probar?
Empieza por tu pico de tráfico actual — revisa en analytics el mayor número de usuarios concurrentes de los últimos 30 días. Prueba a 1x (pico actual), 2x (crecimiento esperado) y 5x (escenario de estrés). Si no tienes datos de analytics, estima a partir del volumen de peticiones: si recibes 100.000 peticiones por hora, son unas 28 req/seg, que se traducen en unos 50-100 usuarios concurrentes según la longitud del journey. Para un servicio nuevo sin datos de tráfico, empieza con 100 usuarios y sube. Prueba siempre en un entorno que replique producción — un staging reducido da resultados engañosos.
¿Qué herramienta de load testing debería elegir?
Para pruebas de API con integración en CI/CD, k6 es la mejor opción — se programa en JavaScript, trae thresholds de pass/fail integrados y se integra con GitHub Actions. Para entornos enterprise con protocolos variados (JMS, SMTP, JDBC), JMeter tiene el soporte de protocolos más amplio y una GUI para perfiles no desarrolladores. Para protocolos custom o casos donde necesitas control total desde código, Locust (Python) es la opción más flexible. Para pruebas rápidas de APIs HTTP, Artillery (Node.js) tiene la configuración YAML más simple. Ten en cuenta la experiencia de tu equipo — un equipo de Python estará más cómodo con Locust, uno de JavaScript con k6. Las cuatro herramientas pueden distribuir la carga entre varios workers cuando un solo generador no es suficiente.
¿Cómo simulo un comportamiento de usuario realista?
Mapea los journeys reales desde tus analytics — qué porcentaje de usuarios navega, busca, ve productos, hace checkout. Asigna pesos a cada journey en el script de prueba (k6: llamadas a group() o pesos de exec, Locust: @task(40)). Añade think time entre peticiones (1-5 segundos) — los usuarios reales no encadenan peticiones sin pausa. Usa datos de prueba realistas — IDs de producto reales, queries reales de tu query log, no strings aleatorios. Varía los datos por petición para que las cachés no distorsionen los resultados. Incluye autenticación — la mayoría del tráfico de producción va autenticado. Prueba el journey completo, no endpoints aislados — el comportamiento del sistema ante una secuencia de peticiones difiere del de llamadas sueltas. Si tienes gestión de sesiones, prueba también la creación y expiración de sesiones.
¿Qué hago si la prueba de carga falla?
Primero, determina qué umbral se superó. Si la tasa de errores es alta, revisa los logs del servidor buscando excepciones y los logs de la base de datos buscando problemas de conexión. Si los tiempos de respuesta son altos, revisa el uso de CPU y memoria durante la prueba — si la CPU llegó al 90%, el servidor está subdimensionado. Si las conexiones de base de datos se agotaron, aumenta el tamaño del pool. Si el sistema crasheó, busca fugas de memoria (ejecuta un soak test) o agotamiento de recursos. Documenta el fallo en el informe con la métrica concreta, el valor real y la causa raíz. Corrige el problema y repite la prueba con el mismo perfil. Hagas lo que hagas, no bajes los umbrales para convertir un fallo en un pase — eso anula su propósito. Si el fallo viene de la infraestructura de pruebas (no de la aplicación), marca el resultado como no concluyente y repite con la infraestructura arreglada.
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 Optimización de Consultas SQL
Checklist de trabajo para afinar consultas SQL lentas: lee el plan de EXPLAIN, corrige índices, elimina llamadas N+1, optimiza joins y paginación, y audita el slow query log — con ejemplos ejecutables para PostgreSQL y MySQL.
RecipeRealizar Load Testing en APIs
Cómo simular tráfico realista, medir tiempos de respuesta e identificar cuellos de botella usando k6 y JMeter para APIs y servicios web.
RecipeTesting de Carga de APIs con k6 y Aserciones Basadas en
Como escribir y ejecutar tests de carga con k6 para medir rendimiento de APIs, validar SLOs e identificar cuellos de botella antes del despliegue a produccion
DocPlantilla de Informe de Pruebas de Carga: Benchmarks y Cuellos de Botella
Una plantilla estandarizada para documentar resultados de pruebas de carga y recomendaciones.