¿Qué son los Test Unitarios?: Cómo garantizar la calidad y fiabilidad del código.

por Pauli

Un requerimiento bastante recurrente en las ofertas de trabajo que he encontrado últimamente es que tengas experiencia con los Test Unitarios. Es un concepto que se escucha bastante cuando uno se dedica a la programación y que usamos a veces sin detenernos mucho a pensarlo.

Los test unitarios son una práctica fundamental para cualquier programador que quiera asegurarse de que su código sea robusto y confiable. Estas pruebas que se realizan en fragmentos de código (llamados unidades) y se ejecutan de manera automatizada para detectar errores y asegurar la calidad del código.

¿Por qué son importantes los test unitarios? En palabras simples, por su capacidad para detectar errores en el código antes de que se conviertan en problemas mayores. Como desarrolladores, cuando escribimos código, es fácil cometer errores o dejar de considerar algunos casos particulares que pueden afectar el funcionamiento del software. Las pruebas unitarias permiten identificar estos errores a tiempo y en consecuencia reduce el costo y el tiempo que se requiere para corregirlos luego.

Además, los test unitarios aseguran que el código implementa correctamente los requisitos y especificaciones definidos para el desarrollo. Esto es especialmente importantes en proyectos grandes y complejos, en los que múltiples programadores trabajan diferentes partes del código. Las pruebas garantizan que el trabajo de cada desarrollador funciona correctamente y que se integra adecuadamente con el resto del proyecto.

Otra ventaja que tiene implementar test unitarios es que ayudan a construir un código más seguro. Al identificar y solucionar problemas de seguridad antes de que se conviertan en vulnerabilidades explotables, se reduce el riesgo de ataques maliciosos y se mejora los aspectos de seguridad del software.

¿Cómo identificar las unidades de código para las pruebas unitarias? Una unidad de código es la parte más pequeña de un programa que puede ser probada de forma aislada, como puede ser una función, un método o una clase entera. Para identificar estas unidades es importante considerar el contexto en el que se utiliza este fragmento del código y los posibles errores que pueden ocurrir.

Una buena práctica es comenzar identificando los puntos críticos del código. Por ejemplo, las partes donde se realizan operaciones complejas, las que manejan entradas o que interactúan con servicios externos. Otro punto que se puede considerar crítico son los casos en los que el código debe manejar datos no esperados.

Una vez identificadas las unidades a probar se debe hacer el diseño de casos de prueba efectivo. Un caso de prueba es una entrada específica que se le da a una unidad para verificar su comportamiento. Idealmente, los casos deben ser diseñados para cubrir todos los posibles escenarios y flujos de trabajo.

Algunos puntos a considerar para el diseño:

  • Verificar las entradas y salidas esperadas. Se debe verificar que la unidad de código produzca la salida correcta según las entradas dadas.
  • Los casos de prueba deben cubrir diferentes conjuntos de datos para asegurarse de que la unidad de código funcione correctamente bajo diferentes condiciones.
  • Hay que considerar los casos límite, así nos aseguramos de que el código maneja correctamente situaciones extremas.
  • Pensar en los errores más comunes que pueden ocurrir, no asumir que las cosas pueden ser obvias, un ejemplo común que he usado y detectado en más de una oportunidad es probar diferentes clases de acentos y la cedilla (ç) en algún campo de texto cuando no se prohíbe explícitamente.

Otra buena práctica es implementas alguna herramienta de pruebas automatizadas ya que pueden facilitar el proceso de las pruebas unitarias. Estas herramientas permiten ejecutar las pruebas de manera automática y pueden ser integradas en el proceso de integración continua. Algunas de las herramientas más comunes que se me vienen a la cabeza son JUnit para Java, PyTest y Unittest para Python y Jest para JavaScript.

Test Unitarios para Python.

Para ilustrar un poco cómo es la aplicación de estos tests, lo cual es muy útil si nunca lo haz hecho, voy a agregar un ejemplo de cómo crear test unitarios en una pequeña función en Python usando Unittest. Lo primero es crear una función, por ejemplo, una función que sume dos números (nada original, lo sé).

def suma(num1, num2):
    return num1 + num2

Ahora, creamos los test unitario. Para este caso necesitamos importar la biblioteca de pruebas unitarias de Python y crear una clase de prueba que herede de unittest.TestCase. Dentro de esta clase, creamos un método de prueba que llame a la función suma() y compruebe si el resultado es el esperado.

import unittest


class TestSuma(unittest.TestCase):
    def test_suma(self):
        self.assertEqual(suma(2, 3), 5)
        self.assertEqual(suma(-1, 1), 0)
        self.assertEqual(suma(0, 0), 0)

En este ejemplo estamos creando una clase llamada TestSuma, dentro de esta clase se crea un método de prueba llamado test_suma(). Dentro de este método se llama la función suma() 3 veces, primero con los parámetros 2 y 3, luego con -1 y 1 y finalmente con los parámetros 0 y 0, y se comprueba que los resultados sean iguales a 5, 0 y 0 respectivamente utilizando el método assetEqual().

Finalmente, para ejecutar las pruebas, se debe llamar al método main() de la biblioteca de pruebas unitarias de Python.

if __name__ == '__main__':
    unittest.main()

Los 3 posibles outputs de las pruebas son OK, FAIL o ERROR. Y en este ejemplo, si la prueba sale bien, te debería mostrar algo como esto:

Como vemos, los test unitarios son una herramienta esencial para cualquier programador que quiera garantizar la calidad y fiabilidad de su trabajo. Es realmente una buena idea aprender a diseñarlas y aprender a usar alguna herramientas para implementarlas en tu código. Y como dicen, la práctica hace al maestro.

También puedes leer