Garantizando la calidad del código, hablemos sobre pruebas y los diferentes tipos

Garantizando la calidad del código, hablemos sobre pruebas y los diferentes tipos

Las pruebas de software son un componente esencial en el proceso de desarrollo de aplicaciones. A través de estas pruebas, los desarrolladores pueden asegurarse de que sus programas funcionen según lo previsto, manteniendo la calidad del código y ofreciendo una experiencia confiable a los usuarios. Más allá de mejorar la calidad del código, las pruebas también ofrecen beneficios significativos a largo plazo, como la reducción de costos y tiempos de desarrollo, porque estamos hablando de recursos tecnológicos y tarde o temprano la deuda técnica te alcanzará y estar preparado con pruebas nunca esta de más.

Beneficios de las pruebas de software

Antes que nada, es importante comprender los beneficios más allá de la calidad del código. Las pruebas tempranas y regulares pueden ayudar a reducir costos y tiempo de desarrollo a largo plazo. Al detectar y corregir errores en las etapas iniciales del proceso, se evita que estos errores se propaguen y se conviertan en problemas más graves en el futuro. Además, las pruebas permiten una mayor confianza en el rendimiento del software, lo que a su vez puede generar una mayor satisfacción del usuario y una mejor reputación para el producto. Yo sé que a muchos de nosotros no nos gusta escribir pruebas y muchas veces tendremos que escribir más código en las pruebas que en las funciones o componentes en sí, pero siempre se debe considerar como parte de la etapa de desarrollo.

Tipos de pruebas y cómo aplicarlas

En este artículo, he tratado de agrupar las pruebas que yo considero más importantes, incluso estan enumeradas en nivel de importancia, hay muchas más por supuesto, pero creo que como parte de una batería de pruebas mínimas de aceptación, esta lista debería ser considerada.

A continuación una porción de código que podríamos utilizar para ejemplificar los diferentes tipos de pruebas:

function calculateTotal(items) {
  if (!Array.isArray(items)) {
    throw new Error('Se esperaba un arreglo de elementos.');
  }

  let total = 0;
  for (const item of items) {
    if (typeof item !== 'number') {
      throw new Error('Los elementos deben ser números.');
    }
    total += item;
  }

  return total;
}

1. Pruebas de funcionalidad básica

Estas pruebas evalúan si las funciones, métodos o clases producen los resultados esperados para diferentes conjuntos de datos de entrada. Un ejemplo de esto sería verificar que una función de suma retorna el valor correcto para varios pares de números.

test('calculateTotal suma los elementos correctamente', () => {
  const items = [1, 2, 3, 4, 5];
  const result = calculateTotal(items);
  expect(result).toBe(15);
});

2. Pruebas de cobertura de código

Las pruebas de cobertura de código se aseguran de que todas las instrucciones y ramificaciones del código sean ejecutadas al menos una vez durante las pruebas. Esto garantiza que todas las partes del código sean probadas y disminuye la probabilidad de errores no detectados, esto en inglés es mejor conocido como "test coverage".

test('calculateTotal ejecuta todas las ramificaciones', () => {
  const items = [1, 2, 3, 4, 5];
  calculateTotal(items);
  expect(true).toBe(true);
  // No necesitamos ninguna aserción aquí para evaluar específicamente las ramificaciones, ya que el propósito es asegurarnos de que todas las ramificaciones se ejecuten.
});

3. Pruebas de casos de borde

Las pruebas de casos de borde evalúan el comportamiento del código con valores extremadamente grandes, pequeños o especiales, éstas pruebas ayudan a detectar posibles errores en condiciones límite.

test('calculateTotal maneja correctamente un arreglo vacío', () => {
  const items = [];
  const result = calculateTotal(items);
  expect(result).toBe(0);
});

4. Pruebas de manejo de errores

Las pruebas de manejo de errores validan que el código maneje adecuadamente excepciones, errores de entrada y situaciones inesperadas.

test('calculateTotal lanza error para elementos no numéricos', () => {
  const items = [1, 'dos', 3];
  expect(() => calculateTotal(items)).toThrow('Los elementos deben ser números.');
});

5. Pruebas de desempeño básico

Las pruebas de desempeño evalúan el tiempo de ejecución y el uso de recursos de las unidades de código. Son especialmente útiles para identificar cuellos de botella y problemas de eficiencia.

test('calculateTotal es eficiente para un gran número de elementos', () => {
  const items = new Array(1000000).fill(1); // Un millón de unos
  const startTime = Date.now();
  const result = calculateTotal(items);
  const endTime = Date.now();
  expect(result).toBe(1000000);
  expect(endTime - startTime).toBeLessThan(100);
  // Verifica que el tiempo sea razonable, esto podria ser debatible
});

6. Pruebas de seguridad básica

Las pruebas de seguridad evalúan posibles vulnerabilidades en el código, especialmente si maneja datos sensibles o realiza operaciones críticas.

test('calculateTotal lanza error al no proporcionar un arreglo', () => {
  expect(() => calculateTotal('no-arreglo')).toThrow('Se esperaba un arreglo de elementos.');
});

Algunos consejos

  • Automatización: Automatiza las pruebas tanto como sea posible. Esto garantiza que las pruebas se realicen de manera consistente y repetible, actualmente esto se puede lograr con mucha facilidad si disparas las pruebas bajo ciertas circunstancias (esto me la la idea para un siguiente artículo).

  • Prueba frecuentemente: Realiza pruebas con regularidad a medida que desarrollas. Esto ayuda a detectar problemas temprano y reduce el esfuerzo de corrección.

  • Pruebas beta con usuarios: Involucra a los usuarios en pruebas beta para obtener comentarios y mejorar la usabilidad antes del lanzamiento. Muchas veces me ha ocurrido que por más que escriba pruebas, mis aplicaciones siempre fallan cuando los usuarios las utilizan, y esto ocurre porque ellos tienen diferentes ideas de como debe funcionar, lo que permite tener perspectivas para futuras pruebas.

Herramientas que podrías utilizar

Para implementar pruebas de software de manera efectiva, puedes aprovechar herramientas como Jest para pruebas unitarias, Testing Library para pruebas de interfaz de usuario y Cypress para pruebas de extremo a extremo (e2e).

Happy coding! :D


Photo by Mika Baumeister on Unsplash

Jack Fiallos

Jack Fiallos

Te gustó este artículo?