Inicio / Artículos / ✅ Tipos de Checks en k6

✅ Tipos de Checks en k6

✅ Tipos de Checks en k6

Tipos de Checks en k6

En k6, los checks son aserciones que permiten validar las respuestas de tus solicitudes HTTP. A continuación, se describen los tipos comunes de checks que puedes usar:

1. Checks de Código de Estado

Verifica que el código de estado de la respuesta sea el esperado.

check(res, {
    'el código de estado es 200': (r) => r.status === 200,
});

Checks de Tiempo de Respuesta

Comprueba que el tiempo de respuesta esté dentro de los límites aceptables.

check(res, {
    'el tiempo de respuesta es menor a 200ms': (r) => r.timings.duration < 200,
});

3. Checks del Cuerpo de la Respuesta

Valida contenido específico en el cuerpo de la respuesta. Útil para comprobar que los datos esperados se devuelvan.

check(res, {
    'el cuerpo contiene "success"': (r) => r.body.includes('success'),
});

// Para respuestas JSON:
const jsonResponse = JSON.parse(res.body);
check(jsonResponse, {
    'los datos existen': (j) => j.data !== undefined,
    'el id del usuario es 1': (j) => j.user.id === 1,
});

4. Checks de Cabeceras

Verifica que ciertas cabeceras estén presentes o tengan valores esperados.

check(res, {
    'Content-Type es application/json': (r) => r.headers['Content-Type'] === 'application/json',
});

5. Checks Combinados

Puedes combinar múltiples checks para validar completamente una respuesta.

check(res, {
    'el código de estado es 200': (r) => r.status === 200,
    'el cuerpo contiene "success"': (r) => r.body.includes('success'),
    'Content-Type es application/json': (r) => r.headers['Content-Type'] === 'application/json',
});

6. Checks con Lógica Personalizada

Implementa lógica personalizada para validaciones más complejas.

function validarUsuario(usuario) {
    return usuario.age > 18 && usuario.active === true;
}

check(jsonResponse, {
    'el usuario es válido': () => validarUsuario(jsonResponse),
});

7. Checks de Manejo de Errores

Valida respuestas específicas de error o códigos de estado para asegurarte de que tu aplicación falle de manera controlada.

if (res.status === 404) {
    check(jsonResponse, {
        'el mensaje de error es correcto': (obj) => obj.error === 'Not Found',
        'el código de error es 404': (obj) => obj.code === 404,
    });
}

8. Validación de JSON Anidado

Cuando trabajes con objetos JSON profundamente anidados, verifica la estructura y el contenido.

check(jsonResponse, {
    'los detalles del usuario son correctos': (obj) => obj.user && obj.user.id === 1 && obj.user.details.age > 18,
    'los títulos de las publicaciones están presentes': (obj) => Array.isArray(obj.posts) && obj.posts.every(post => post.title !== undefined),
});

9. Combinar Condiciones

Puedes combinar checks usando operadores lógicos.

check(res, {
    'el código de estado es 200 y Content-Type es JSON': (r) => r.status === 200 && r.headers['Content-Type'] === 'application/json',
});

10. Validación en Bucles

Verifica múltiples elementos en un arreglo.

const validPosts = jsonResponse.posts.filter(post => post.published);

check(validPosts, {
    'todas las publicaciones publicadas tienen títulos': () => validPosts.every(post => post.title !== undefined),
});

11. Checks con Funciones Personalizadas

Encapsula lógica compleja en funciones reutilizables.

function esUsuarioValido(usuario) {
    return usuario && usuario.id > 0 && usuario.active === true && usuario.roles.includes('user');
}

check(jsonResponse, {
    'el usuario es válido': () => esUsuarioValido(jsonResponse.user),
});

12. Checks Condicionales

Realiza checks que dependan de resultados previos.

let usuarioExiste = false;

if (res.status === 200) {
    const jsonResponse = JSON.parse(res.body);
    usuarioExiste = jsonResponse.user !== null;
}

check(res, {
    'el usuario existe': () => usuarioExiste,
});

13. Uso de Umbrales para Rendimiento y Validaciones

Puedes combinar verificaciones (checks) con umbrales de rendimiento para garantizar que las respuestas cumplan con los tiempos establecidos.

import { check } from 'k6';

check(res, {
    'el tiempo de respuesta es aceptable': (r) => r.timings.duration < 200,
    'el código de estado es 200': (r) => r.status === 200,
});

// Establecer umbrales para aplicar requerimientos de rendimiento
export const thresholds = {
    'response_time': ['p(95)<200'], // El 95% de las respuestas deben ser menores a 200ms
};

14. Verificación de Patrones Específicos en la Respuesta

Para APIs que devuelven patrones específicos (por ejemplo, IDs con formato específico), puedes usar expresiones regulares.

const jsonResponse = JSON.parse(res.body);

check(jsonResponse, {
    'el ID de usuario tiene un patrón válido': (obj) => /^user_\d+$/.test(obj.id),
});

15. Rendimiento y Validación Combinados

Puedes medir tiempos de respuesta mientras validas el contenido de las respuestas.

check(res, {
    'el tiempo de respuesta es aceptable': (r) => r.timings.duration < 300,
    'el cuerpo de la respuesta es válido': (r) => {
        const jsonResponse = JSON.parse(r.body);
        return jsonResponse && jsonResponse.success === true;
    },
});

16. Validación Cruzada de Datos

Cuando trabajas con múltiples puntos de datos, puedes validar las relaciones entre ellos.

const userResponse = http.get('https://api.example.com/users/1');
const postResponse = http.get('https://api.example.com/posts');

check(userResponse, {
    'el usuario existe': (r) => r.status === 200,
});

check(postResponse, {
    'todas las publicaciones pertenecen al usuario': (r) => {
        const posts = r.json();
        return posts.every(post => post.userId === userResponse.json().id);
    },
});

17. Resultados Agregados de Verificaciones

Puedes agregar resultados de múltiples verificaciones y proporcionar un resumen consolidado.

let checkResults = {
    validStatus: false,
    validContent: false,
};

check(res, {
    'el estado es 200': (r) => {
        checkResults.validStatus = r.status === 200;
        return checkResults.validStatus;
    },
    'la respuesta contiene datos esperados': (r) => {
        const jsonResponse = JSON.parse(r.body);
        checkResults.validContent = jsonResponse.data !== undefined;
        return checkResults.validContent;
    },
});

// Registrar resultados de las verificaciones
console.log(`Resultados: Estado válido? ${checkResults.validStatus}, Contenido válido? ${checkResults.validContent}`);

18. Validación con Librerías Externas

Si necesitas validaciones más avanzadas, puedes usar librerías externas como Ajv para validación de esquemas JSON.

import Ajv from 'ajv';
import { check } from 'k6';

const ajv = new Ajv();
const schema = {
    type: 'object',
    properties: {
        id: { type: 'integer' },
        name: { type: 'string' },
    },
    required: ['id', 'name'],
};

const jsonResponse = JSON.parse(res.body);
const validate = ajv.compile(schema);
const valid = validate(jsonResponse);

check(res, {
    'la respuesta cumple con el esquema': () => valid,
});

19. Validaciones Dinámicas

Para asegurar que los valores sigan reglas específicas, como IDs secuenciales o rangos de tiempo válidos.

const jsonResponse = JSON.parse(res.body);
const ids = jsonResponse.items.map(item => item.id);

check(res, {
    'los IDs son secuenciales': () => {
        return ids.every((id, index) => index === 0 || id === ids[index - 1] + 1);
    },
    'las marcas de tiempo son válidas': () => {
        return jsonResponse.items.every(item => {
            const date = new Date(item.timestamp);
            return date instanceof Date && !isNaN(date);
        });
    },
});

20. Validación Basada en Tiempos

Puedes validar que las operaciones no devuelvan datos antiguos.

const jsonResponse = JSON.parse(res.body);
const now = Date.now();

check(res, {
    'los datos son recientes': () => {
        return jsonResponse.items.every(item => {
            const itemDate = new Date(item.updatedAt).getTime();
            return now - itemDate < 24 * 60 * 60 * 1000; // Dentro de 24 horas
        });
    },
});

21. Chequeo Agregado de Métricas

Combina validaciones funcionales con métricas de rendimiento para un análisis más completo.

let totalChecksPassed = 0;

check(res, {
    'el estado es 200': (r) => {
        const result = r.status === 200;
        totalChecksPassed += result ? 1 : 0;
        return result;
    },
    'la respuesta es válida': (r) => {
        const jsonResponse = JSON.parse(r.body);
        const result = jsonResponse.success === true;
        totalChecksPassed += result ? 1 : 0;
        return result;
    },
});

// Registrar total de verificaciones aprobadas
console.log(`Verificaciones aprobadas: ${totalChecksPassed}`);

22. Ejecución Condicional Basada en Respuesta

Ejecuta dinámicamente verificaciones según la respuesta de una llamada previa.

const res = http.get('https://api.example.com/feature');

if (res.status === 200) {
    const jsonResponse = JSON.parse(res.body);
    check(jsonResponse, {
        'la característica está habilitada': (obj) => obj.enabled === true,
    });
} else {
    check(res, {
        'la característica está deshabilitada': (r) => r.status === 404,
    });
}

23. Integración con Herramientas de Monitoreo Externas

Envía resultados de métricas a herramientas como Grafana o Prometheus.

import { Trend } from 'k6/metrics';

let responseTimeTrend = new Trend('response_time');

export default function () {
    let res = http.get('https://api.example.com/resource');
    responseTimeTrend.add(res.timings.duration);

    check(res, {
        'el estado es 200': (r) => r.status === 200,
    });
}

24. Validación de Paginación

Comprueba que la lógica de paginación funcione correctamente.

const firstPage = http.get('https://api.example.com/items?page=1');
const secondPage = http.get('https://api.example.com/items?page=2');

check(firstPage, {
    'la primera página tiene elementos': (r) => r.json().items.length > 0,
});

check(secondPage, {
    'la segunda página tiene elementos': (r) => r.json().items.length > 0,
});

const firstItems = firstPage.json().items;
const secondItems = secondPage.json().items;

check(secondItems, {
    'los elementos de la segunda página son diferentes': () => {
        return !firstItems.some(item => secondItems.some(secondItem => secondItem.id === item.id));
    },
});

26. Validación de Respuestas en Paralelo

Para escenarios en los que necesitas validar múltiples respuestas de forma simultánea, puedes usar promesas.

import { check, group } from 'k6';
import http from 'k6/http';

export default function () {
    const urls = [
        'https://api.example.com/users/1',
        'https://api.example.com/posts',
        'https://api.example.com/comments',
    ];

    // Hacer múltiples solicitudes en paralelo
    const responses = http.batch(urls.map(url => ['GET', url]));

    // Verificar cada respuesta
    responses.forEach((res, idx) => {
        check(res, {
            [`response ${idx + 1} is valid`]: (r) => r.status === 200,
        });
    });
}

Dato importante:

r: El objeto pasado al callback de la función check: En el callback de la función check, r es el mismo objeto de respuesta que res. El propósito de r es simplemente proporcionar acceso a los datos de res dentro de la función callback, por lo que r es una referencia al mismo objeto que res en este caso.

Entonces, ¿por qué se usan nombres diferentes? El uso de nombres distintos es simplemente una convención de estilo de código. Se usa res fuera de la función check y r dentro de la función callback. Esto puede deberse a:

  • 1 Legibilidad: Dentro del callback, un nombre corto como r es más práctico para evitar repetir res cada vez.
  • 2 Scope local: El nombre r es local al callback y no afecta el uso de res en el nivel externo.

¿Es necesario usar nombres diferentes? No, puedes usar el mismo nombre (res) si lo prefieres:

const res = http.get('https://api.example.com/resource');
check(res, {
    'el tiempo de respuesta es aceptable bajo carga': (res) => res.timings.duration < 500,
});