Si eres un desarrollador, sin importar tu lenguaje favorito, seguramente haz escuchado hablar sobre testing (pruebas). Existen distintos tipos, como unitarias, de integración, etc., pero podemos resumir (al menos para este post) que se trata de un proceso por el cual nos aseguramos que el funcionamiento de nuestro código sea el esperado, y que mejor aún si lo podemos realizar de manera automática.
En RO Solutions desarrollamos principalmente en Javascript, por lo que nos vamos a enfocar específicamente en este lenguaje y que significa hacer testing automatizado en el.
¿Qué son?
Una prueba automatizada es un pedazo de código que mostrara un error cuando encuentre un funcionamiento o resultado inesperado. Pongamos un ejemplo:
const sum = (a, b) => a + b;
const result = sum(5, 4);
const expected = 10;
if (result !== expected) {
throw new Error(`${result} no es igual a ${expected}`);
}
En el código anterior, contamos con una función que suma dos valores, una variable donde asignamos el resultado esperado y si el resultado de la suma no coincide con el resultado esperado arrojamos un error.
Esta es la forma más fundamental de una prueba, sin embargo, no parece muy practico, el mensaje de error no nos indica la causa del error y estamos añadiendo código que no aporta ninguna funcionalidad a parte de las pruebas. ¿Cómo podemos mejorar esto?
Assertion Library
¿Qué es una librería de aserciones (assertion library)?, ¿qué es una aserción?. Podemos definir una aserción como una afirmación o validación de un supuesto, por lo que una librería de aserciones es un conjunto de funciones que nos permitan hacer estas validaciones.
Vamos a poner un ejemplo utilizando el mismo caso anterior:
const {sum, subtract} = require('../math');
const resultSum = sum(5, 4);
const resultSubtract = subtract(5,4);
const expected = 10;
// Aquí estamos corriendo nuestras pruebas
expect(resultSum).toEqual(expected);
expect(resultSubtract).toBeLowerThan(expected);
// Assertion library
function expect(actual) {
return {
toEqual(expected) {
if (actual !== expected) {
throw new Error(`${actual} is not equal to ${expected}`);
}
},
toBeLowerThan(expected) { ... },
toBeGreaterThan(expected) { ... },
}
}
Similar al caso anterior, vamos a probar el resultado de una función de suma y otra de resta; esta vez vamos a simular un documento de pruebas (un documento donde se encuentran todas nuestras pruebas por separado del código de implementación) el cual vamos a ejecutar cuando deseemos probar nuestro código.
Comenzamos por importar las funciones, las ejecutamos y verificamos los resultados haciendo uso de una nueva función llamada expect (nuestra librería de aserciones) que retorna distintas funciones (aserciones), mismas que usaremos para realizar las pruebas. En esencia esa es la función de una librería, proveer herramientas, lo más abstractas posibles, que nos permitan realizar las validaciones o aserciones que necesitamos.
Testing Framework
Nuestras pruebas automatizadas van tomando forma, sin embargo aun nos encontramos con limitaciones, por ejemplo, si una prueba falla, las siguientes pruebas no se realizaran e identificar cual dio el error podría ser complicado pues es la misma función quien arroja el error. Es el trabajo de un marco de pruebas (testing framework) ayudar a los desarrolladores a identificar rápidamente donde se encuentran los problemas. Vamos a crear el nuestro.
function test(title, callback) {
try {
callback();
console.log(`✓ ${title}`);
} catch (error) {
console.error(`x ${title}`);
console.error(error);
}
}
Esta pequeña función nos va permitir encapsular y realizar distintas pruebas, además de mostrar los errores de forma que nos permitan solucionar los problemas más fácilmente. Vamos a verla en acción continuando con nuestro ejemplo.
const {sum, subtract} = require('../math');
const {test, expect} = require('../testing');
test('sum adds numbers', () => {
const result = sum(5, 4);
const expected = 10;
expect(result).toEqual(expected);
});
test('subtract subtracts numbers', () => {
const result = subtract(5, 4);
const expected = 1;
expect(result).toEqual(expected);
});
El resultado se muestra en la imagen a continuación. Donde vemos que fallo la primer prueba y la segunda fue ejecutada con éxito.
Conclusión
Como vemos, nuestra assertion library y testing framework pueden ser reutilizados y al final nuestras pruebas quedan reducidas únicamente al código que necesitamos para realizar las aserciones, con la seguridad de que nos entregaran mensajes claros que nos ayuden a resolver los problemas.
El propósito de este post no es motivarlos a escribir su propio testing framework o enseñar como hacer el mejor testing a nuestro código, sino el de explicar que son las pruebas automatizadas y como funcionan, para que la próxima vez que utilicen Jest o alguno de los sabores de Testing Library puedan entender y utilizar mejor esas herramientas.