Pruebas de rendimiento realistas para APIs Laravel/Next.js con k6 y Playwright
Escenario real: tu agente dice que los tests pasan, pero la API se cae en producción
Imagina que tu pipeline de CI ejecuta una suite de pruebas unitarias y, al final, el agente te devuelve el mensaje habitual: “Tests passed”. Todo parece estar bajo control, sin embargo, cuando el cliente hace la primera petición después del despliegue, la respuesta tarda más de 5 segundos o, peor aún, la API lanza un error 500. El problema no está en la lógica de negocio, sino en cómo la API se comporta bajo carga real.
En este punto, la regla que todo agente debe cumplir es:
Si el agente solo verifica que los tests terminan sin errores, no está garantizando que la API será usable en producción; el agente debe medir latencia y degradación bajo carga representativa.
Esta regla es transferible a cualquier stack: Laravel para el backend, Next.js para el frontend, y cualquier agente que invoque tus pruebas. A partir de ahora, el agente no solo informará “green”, sino que también entregará métricas de rendimiento que permitan decidir si el despliegue es seguro.
Arquitectura mínima con Docker
Para que el agente pueda ejecutar k6 y Playwright sin depender de la máquina del desarrollador, usamos contenedores Docker. La ventaja es que el entorno es idéntico en CI y en tu máquina local, y puedes versionar la configuración junto al código.
Dockerfile base para k6
FROM loadimpact/k6:latest
WORKDIR /scripts
COPY ./k6 /scripts
Dockerfile base para Playwright
FROM mcr.microsoft.com/playwright:latest
WORKDIR /tests
COPY ./playwright /tests
RUN npm ci
docker‑compose.yml que une todo
version: "3.9"
services:
laravel:
build: ./laravel
ports:
- "8000:80"
environment:
- APP_ENV=local
- DB_CONNECTION=sqlite
nextjs:
build: ./nextjs
ports:
- "3000:3000"
k6:
build: ./k6
depends_on:
- laravel
- nextjs
playwright:
build: ./playwright
depends_on:
- laravel
- nextjs
Con este docker‑compose.yml el agente puede lanzar docker compose up --build -d y tener una stack completa lista para los tests de carga.
k6: pruebas de carga enfocadas en la API Laravel
k6 es una herramienta de línea de comandos que permite escribir scripts en JavaScript para simular miles de usuarios concurrentes. Lo importante es que el script refleje los flujos reales que tu agente ejecuta en producción.
Ejemplo de script k6 para la ruta de autenticación
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend } from 'k6/metrics';
const loginTime = new Trend('login_response_time');
export const options = {
stages: [
{ duration: '30s', target: 20 }, // ramp‑up a 20 VUs
{ duration: '1m', target: 20 }, // mantenerse
{ duration: '30s', target: 0 }, // ramp‑down
],
thresholds: {
login_response_time: ['p(95)<500'], // 95% < 500 ms
http_req_failed: ['rate<0.01'], // menos del 1 % fallos
},
};
export default function () {
const payload = JSON.stringify({
email: 'test@example.com',
password: 'secret',
});
const params = {
headers: { 'Content-Type': 'application/json' },
};
const res = http.post('http://localhost:8000/api/login', payload, params);
loginTime.add(res.timings.duration);
check(res, {
'status es 200': (r) => r.status === 200,
'token presente': (r) => r.json('token') !== undefined,
});
sleep(1);
}
El agente debe ejecutar este script dentro del contenedor k6 y capturar los resultados. La métrica login_response_time y el umbral p(95)<500 son la evidencia que el agente necesita para decidir si el despliegue es aceptable.
Playwright: pruebas end‑to‑end con carga ligera
Mientras k6 se centra en la API, Playwright permite validar que el frontend Next.js sigue funcionando cuando el backend está bajo presión. La idea no es generar carga masiva con Playwright (no es su fuerte), sino combinar una carga moderada con verificaciones de UI.
Script Playwright que verifica la página de inicio y una petición a la API
const { test, expect } = require('@playwright/test');
test('Home page loads and fetches data under load', async ({ page }) => {
// Simular un usuario que abre la página
await page.goto('http://localhost:3000');
// Verificar que el título está presente
await expect(page).toHaveTitle(/Mi App/);
// Esperar a que la tabla de datos se rellene (consulta a Laravel)
const rows = await page.locator('table#items tbody tr');
await expect(rows).toHaveCount(10); // esperamos 10 items en fixtures
// Medir tiempo de respuesta de la petición fetch interna
const [response] = await Promise.all([
page.waitForResponse('**/api/items'),
page.click('button#refresh'), // botón que dispara fetch
]);
const timing = response.timing();
console.log('fetch duration ms:', timing.responseStart - timing.requestStart);
expect(timing.responseStart - timing.requestStart).toBeLessThan(400);
});
El agente ejecuta npx playwright test dentro del contenedor playwright. Si el test falla, el agente informa que la UI está degradada bajo carga, aunque la API por sí sola pudiera haber pasado los umbrales de k6.
Integración directa con Laravel
Laravel ya incluye un servidor de pruebas y una base de datos en memoria (SQLite). Para que k6 y Playwright trabajen con datos consistentes, preparamos fixtures en una migración de prueba.
Seeder de ejemplo
use Illuminate\Database\Seeder;
use App\Models\User;
use App\Models\Item;
class TestDataSeeder extends Seeder
{
public function run()
{
User::factory()->create([
'email' => 'test@example.com',
'password' => bcrypt('secret'),
]);
Item::factory()->count(10)->create();
}
}
Antes de levantar el contenedor Laravel, el Dockerfile ejecuta:
RUN php artisan migrate --seed --class=TestDataSeeder
De esta forma, tanto k6 como Playwright consumen el mismo conjunto de datos, evitando falsos positivos.
Integración directa con Next.js
Next.js puede servir tanto páginas estáticas como API routes. Para que la UI sea predecible durante los tests, usamos next export en modo producción y mantenemos una copia de los datos en public/mock para los casos donde la API no está disponible.
next.config.js simplificado
module.exports = {
output: 'standalone',
images: { unoptimized: true },
async rewrites() {
return [
{ source: '/api/:path*', destination: 'http://localhost:8000/api/:path*' },
];
},
};
El agente no necesita cambiar nada; simplemente ejecuta npm run build && npm start dentro del contenedor nextjs. La regla de reescritura redirige las peticiones API a Laravel, lo que permite probar la integración completa.
Ejecución automatizada desde el agente
El agente se encarga de orquestar los pasos, pero sin ser un orquestador complejo. Un script Bash simple basta para lanzar todo y recoger los resultados.
run-tests.sh (ejemplo)
#!/usr/bin/env bash
set -e
# Levantar stack
docker compose up -d --build
# Esperar a que los servicios estén listos
until curl -s http://localhost:8000/api/health | grep \"ok\"; do
echo "Esperando a Laravel..."
sleep 2
done
until curl -s http://localhost:3000/api/health | grep \"ok\"; do
echo "Esperando a Next.js..."
sleep 2
done
# Ejecutar k6
docker compose run --rm k6 run /scripts/login_test.js --summary-export=summary.json
# Ejecutar Playwright
docker compose run --rm playwright npx playwright test --reporter=line
# Capturar métricas de k6
LOGIN_P95=$(jq '.metrics.login_response_time.p95' summary.json)
# Decisión del agente
if (( $(echo "$LOGIN_P95 < 500" | bc -l) )); then
echo "✅ k6: latencia dentro del umbral"
else
echo "❌ k6: latencia demasiado alta ($LOGIN_P95 ms)"
exit 1
fi
echo "✅ Todas las pruebas pasaron. Despliegue aprobado."
El agente interpreta la salida del script. Si cualquier paso devuelve error (por ejemplo, el umbral de k6 se supera), el agente informa “Tests failed” con la causa específica, lo que permite al desarrollador actuar rápidamente.
Reporte y visualización ligera
Para que el agente no solo entregue texto plano, podemos generar un informe HTML sencillo usando los resultados de k6 y Playwright. La generación se hace dentro del contenedor k6 con k6 html-report (si está instalado) o mediante un pequeño script Node.
script node para combinar métricas
const fs = require('fs');
const summary = JSON.parse(fs.readFileSync('summary.json'));
const report = `
<h2>Reporte de rendimiento</h2>
<p>Latencia p95 de login: ${summary.metrics.login_response_time.p95} ms</p>
<p>Tasa de fallos: ${(summary.metrics.http_req_failed.rate * 100).toFixed(2)} %</p>
`;
fs.writeFileSync('report.html', report);
El agente adjunta report.html al mensaje final, proporcionando al equipo una visión rápida sin necesidad de abrir herramientas externas.
Cierre: el agente ahora entrega datos, no solo un sí verde
Al aplicar la regla de medir latencia bajo carga real, tu agente deja de ser un simple verificador de compilación y se convierte en un guardián de la experiencia del usuario. Con Docker, k6 y Playwright puedes crear un entorno reproducible que cubra tanto la API Laravel como el frontend Next.js, y todo ello con scripts que cualquier desarrollador puede ejecutar en su máquina local.
La próxima entrega mostrará cómo integrar estas pruebas en un flujo de GitHub Actions sin añadir complejidad al agente. Hasta entonces, pon a prueba tu API con los scripts presentados y verifica que tu agente realmente te avisa cuando la latencia supera los límites aceptables.