Este HowTo, explica brevemente qué son, para que sirven y cómo deben escribirse, Pruebas Unitarias con Python.
Qué son y para qué sirven
Una Prueba Unitaria Unit Test
, es la forma más común de probar nuestro código. Utilizaremos una Prueba Unitaria, para verificar partes de nuestros scripts o programas con las que poder comprobar si estos se comportan como se esperan.
Las escribiremos junto con el código que deseamos comprobar, para aseguramos, que esa parte de código hace lo que tiene que hacer. Es importante saber que las Pruebas Unitarias, nunca deben modificar el entorno de producción, por lo que necesitamos crear un entorno de pruebas para ello.
Para poder entender cómo funciona una Prueba Unitaria, primero vamos a probar una función desde el Intérprete de Python. Para ello importaremos una función usando la palabra clave from. Supongamos que nuestro fichero se llama calculos.py y queremos probar una función llamada iva() dentro de este.
1 2 3 |
def iva(value): result = round(float(value)*0.21, 2) return result |
Para poder ejecutar y probar la función desde nuestro intérprete de Python, sin tener la necesidad de escribirla de nuevo, vamos a escribir la siguiente línea de código.
1 2 3 4 |
$ python3 >>> from calculos import iva >>> iva(20) 4.2 |
Una vez la hemos importado, podemos realizar nuestra primera Prueba Unitaria manual sobre la función, sin la necesidad de tocar el entorno de producción de nuestro programa. Una vez sabemos la funcionalidad básica de una Prueba Unitaria, lo siguiente es hacer que estas pruebas se realicen de forma automática, sin tener que hacerlas manualmente nosotros mismos.
Escribiendo Pruebas Unitarias en Python
Para que una Prueba Unitaria sea «automática«, necesitamos escribirla junto con el código que queremos probar, por lo que para ello vamos a crear un nuevo archivo con la Prueba. A este nuevo fichero lo llamaremos con el mismo nombre del módulo que queremos probar, seguido del sufijo _test.
Siguiendo con nuestro ejemplo anterior, lo llamaremos calculos_test.py. Una vez creado lo editamos, incluimos el Shebang e importamos la función que queremos probar de la misma manera que lo hicimos anteriormente desde el intérprete de Python.
1 2 3 |
#! /usr/bin/env python3 from calculos import iva |
Para ayudarnos a crear Pruebas Unitarias, Python nos proporciona un módulo llamado unittest
. Este módulo incluye clases y métodos para crear Pruebas Unitarias de nuestro código, de forma más sencilla.
1 |
import unittest |
El módulo unittest
nos proporciona la clase TestCase
, que incluye una gran cantidad de métodos para realizar pruebas. Para usar esta clase, vamos a crear la nuestra propia y haremos que herede toda las funcionalidades de la clase de prueba (TestCase).
1 |
class TestCalculos(unittest.TestCase): |
Todos los métodos que definamos en la clase que acabamos de crear y que comiencen por el prefijo test_ se convertirán automáticamente en Pruebas y podrán ejecutarse en dicho entorno de Pruebas.
Para entender este proceso, vamos a crear un nuevo método llamado test_basic(); la variable testcase contiene el valor para la prueba y la variable expected contiene el valor esperado. A continuación usando el método assertEqual(), heredado de la clase TestCase, verificamos que el resultado es lo que esperábamos.
1 2 3 4 5 |
class TestIva(unittest.TestCase): def test_basico(self): testcase = 20 expected = 4.2 self.assertEqual(iva(testcase), expected) |
El método, assertEqual()
, básicamente lo que hace es decirnos si los argumentos declarados son iguales. Si el resultado devuelto es True la prueba es correcta y False en caso contrario.
Una vez creada nuestra primera Prueba Unitaria, necesitamos llamarla. Para ello llamamos a la función unittest.main(), que ejecutará la prueba por nosotros.
1 |
unittest.main() |
Por último para realizar la prueba de forma automatizada, tan sólo necesitamos dar permisos de ejecución al fichero y ejecutarlo.
1 2 3 4 5 6 7 |
$ chmod +x calculos_test.py $ ./calculos_test.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK |
Casos extremos (Edge Cases)
Ya sabemos como crear Pruebas Unitarias en Python, pero éstas sólo contienen un Caso de Prueba. Para crear más Pruebas necesitamos algo de creatividad e imaginación, por ejemplo, ¿qué ocurre si el usuario no introduce ningún dato?, veamos como crear un nuevo método que lo compruebe.
1 2 3 4 |
def test_empty_value(self): testcase = "" expected = 0 self.assertEqual(iva(testcase), expected) |
En este caso, le estamos diciendo que esperamos que nuestra función devuelva una cadena vacía, si el valor recibido no es introducido. Cuando ejecutamos la Prueba, detectamos un error en nuestro código.
1 2 3 |
ValueError: could not convert string to float: '' ---------------------------------------------------------------------- Ran 2 tests in 0.001s |
A esto lo llamamos un caso extremo, que son aquellos que producen errores no esperados y se encuentran con la entrada y salida de datos. Para que estos errores no se produzcan, debemos modificar nuestro script principal.
1 2 3 4 5 6 7 |
#! /usr/bin/env python3 def iva(value): if value == "": return 0 result = round(float(value)*0.21, 2) return result |
Una vez lo hemos modificado, si ejecutamos de nuevo la Prueba Unitaria, esta debe pasarse correctamente.
1 2 3 4 5 6 |
./calculos_test.py .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK |
Otros casos extremos que nos podemos encontrar son pasarle letras a nuestra función, números negativos, números excesivamente grandes para poder manejarlos, etc … Y recuerda que la automatización, siempre debería devolver errores, nunca fallar silenciosamente.
Listado de afirmaciones:
- assertEqual(a, b) … a ==b
- assertNotEqual(a, b) … a != b
- assertTrue(x) … bool(x) is True
- assertFalse(x) … bool(x) is False
- assertIs(a, b) … a is b
- assertIsNot(a, b) … a is not b
- assertIsNone(x) … x is None
- assertIsNotNone(x) … x is not None
- assertIn(a, b) … a in b
- assertNotIn(a, b) … a not in b
- assertIsInstance(a, b) … isinstance(a, b)
- assertNotIsInstance(a, b) … not isinstance(a, b)
Documentación de interés
- Módulo unittest
- Test Unitarios desde línea de comandos.
- Diseño de Patrones.