Una aserción es la comprobación que decide si una prueba pasa o falla. En unittest, las aserciones se escriben mediante métodos de unittest.TestCase, como assertEqual, assertTrue o assertRaises.
En este tema veremos las aserciones más usadas, cuándo conviene usar cada una y cómo leer mejor la intención de una prueba.
Crea un proyecto nuevo:
mkdir aserciones-unittest-demo
cd aserciones-unittest-demo
En este ejemplo no instalaremos paquetes, porque unittest ya viene incluido con Python.
Crea un archivo llamado usuarios.py:
def crear_usuario(nombre, edad, email=None):
if not nombre or not nombre.strip():
raise ValueError("El nombre es obligatorio")
if edad < 0:
raise ValueError("La edad no puede ser negativa")
return {
"nombre": nombre.strip().title(),
"edad": edad,
"email": email,
"activo": edad >= 18,
"roles": ["usuario"],
}
def obtener_dominio(email):
if "@" not in email:
raise ValueError("Email inválido")
return email.split("@")[1].lower()
def calcular_promedio(valores):
if not valores:
return None
return sum(valores) / len(valores)
Este archivo nos permite probar diccionarios, listas, valores booleanos, None, excepciones, cadenas y números decimales.
Crea test_usuarios.py:
import unittest
from usuarios import calcular_promedio, crear_usuario, obtener_dominio
class TestUsuarios(unittest.TestCase):
pass
Agregaremos los métodos de prueba dentro de esta clase.
assertEqual comprueba que dos valores sean iguales. Es una de las aserciones más usadas.
def test_crear_usuario_normaliza_nombre(self):
usuario = crear_usuario(" ana ", 20)
self.assertEqual(usuario["nombre"], "Ana")
Si el nombre obtenido no es "Ana", la prueba falla y muestra la diferencia.
assertNotEqual comprueba que dos valores sean diferentes.
def test_usuario_no_conserva_espacios_en_nombre(self):
usuario = crear_usuario(" ana ", 20)
self.assertNotEqual(usuario["nombre"], " ana ")
Conviene usarla cuando la diferencia forma parte clara del comportamiento esperado.
assertTrue comprueba que una expresión sea verdadera.
def test_usuario_mayor_de_edad_esta_activo(self):
usuario = crear_usuario("Ana", 18)
self.assertTrue(usuario["activo"])
Es útil para valores booleanos o condiciones que deben cumplirse.
assertFalse comprueba que una expresión sea falsa.
def test_usuario_menor_de_edad_no_esta_activo(self):
usuario = crear_usuario("Luis", 17)
self.assertFalse(usuario["activo"])
En este caso, la regla indica que una persona menor de 18 años no queda activa.
assertIsNone comprueba que un valor sea exactamente None.
def test_promedio_de_lista_vacia_es_none(self):
resultado = calcular_promedio([])
self.assertIsNone(resultado)
Es más expresivo que escribir assertEqual(resultado, None).
assertIsNotNone comprueba que un valor no sea None.
def test_usuario_tiene_lista_de_roles(self):
usuario = crear_usuario("Ana", 20)
self.assertIsNotNone(usuario["roles"])
assertIn comprueba que un valor esté dentro de una colección o cadena.
def test_usuario_tiene_rol_usuario(self):
usuario = crear_usuario("Ana", 20)
self.assertIn("usuario", usuario["roles"])
También puede usarse con cadenas:
def test_dominio_contiene_punto(self):
dominio = obtener_dominio("ana@example.com")
self.assertIn(".", dominio)
assertNotIn comprueba que un valor no esté dentro de una colección o cadena.
def test_usuario_no_tiene_rol_admin_por_defecto(self):
usuario = crear_usuario("Ana", 20)
self.assertNotIn("admin", usuario["roles"])
assertRaises comprueba que una operación lance una excepción esperada.
def test_nombre_vacio_lanza_error(self):
with self.assertRaises(ValueError):
crear_usuario(" ", 20)
Esta forma con with es clara y permite encerrar solamente la línea que debe producir el error.
A veces queremos comprobar también el mensaje de la excepción:
def test_email_invalido_lanza_mensaje_claro(self):
with self.assertRaises(ValueError) as contexto:
obtener_dominio("correo-invalido")
self.assertEqual(str(contexto.exception), "Email inválido")
Esto debe usarse cuando el mensaje forma parte importante del comportamiento esperado.
Los números decimales pueden tener pequeñas diferencias por la forma en que las computadoras representan valores de punto flotante. Para estos casos existe assertAlmostEqual.
def test_promedio_con_decimales(self):
resultado = calcular_promedio([0.1, 0.2])
self.assertAlmostEqual(resultado, 0.15)
Es útil para resultados con decimales, porcentajes, promedios y cálculos similares.
assertGreater comprueba que un valor sea mayor que otro. assertLess comprueba que sea menor.
def test_edad_de_usuario_es_mayor_que_cero(self):
usuario = crear_usuario("Ana", 20)
self.assertGreater(usuario["edad"], 0)
def test_usuario_menor_tiene_edad_menor_a_18(self):
usuario = crear_usuario("Luis", 17)
self.assertLess(usuario["edad"], 18)
assertIsInstance comprueba que un valor sea instancia de un tipo determinado.
def test_crear_usuario_devuelve_diccionario(self):
usuario = crear_usuario("Ana", 20)
self.assertIsInstance(usuario, dict)
No conviene abusar de esta aserción. Es útil cuando el tipo devuelto forma parte del contrato que queremos garantizar.
La mayoría de las aserciones permiten agregar un mensaje final. Ese mensaje se muestra si la prueba falla.
def test_usuario_adulto_debe_quedar_activo(self):
usuario = crear_usuario("Ana", 18)
self.assertTrue(
usuario["activo"],
"Un usuario de 18 años o más debe quedar activo",
)
Úsalos cuando ayuden a entender la regla de negocio. No hace falta agregar mensajes obvios a todas las aserciones.
El archivo test_usuarios.py puede quedar así:
import unittest
from usuarios import calcular_promedio, crear_usuario, obtener_dominio
class TestUsuarios(unittest.TestCase):
def test_crear_usuario_normaliza_nombre(self):
usuario = crear_usuario(" ana ", 20)
self.assertEqual(usuario["nombre"], "Ana")
def test_usuario_mayor_de_edad_esta_activo(self):
usuario = crear_usuario("Ana", 18)
self.assertTrue(usuario["activo"])
def test_usuario_menor_de_edad_no_esta_activo(self):
usuario = crear_usuario("Luis", 17)
self.assertFalse(usuario["activo"])
def test_promedio_de_lista_vacia_es_none(self):
resultado = calcular_promedio([])
self.assertIsNone(resultado)
def test_usuario_tiene_rol_usuario(self):
usuario = crear_usuario("Ana", 20)
self.assertIn("usuario", usuario["roles"])
def test_usuario_no_tiene_rol_admin_por_defecto(self):
usuario = crear_usuario("Ana", 20)
self.assertNotIn("admin", usuario["roles"])
def test_nombre_vacio_lanza_error(self):
with self.assertRaises(ValueError):
crear_usuario(" ", 20)
def test_email_invalido_lanza_mensaje_claro(self):
with self.assertRaises(ValueError) as contexto:
obtener_dominio("correo-invalido")
self.assertEqual(str(contexto.exception), "Email inválido")
def test_promedio_con_decimales(self):
resultado = calcular_promedio([0.1, 0.2])
self.assertAlmostEqual(resultado, 0.15)
def test_crear_usuario_devuelve_diccionario(self):
usuario = crear_usuario("Ana", 20)
self.assertIsInstance(usuario, dict)
if __name__ == "__main__":
unittest.main()
Desde la carpeta del proyecto:
python -m unittest -v
La salida debe mostrar todas las pruebas con resultado ok.
Una buena aserción expresa la intención de la prueba. Por ejemplo, si esperamos None, es más claro usar assertIsNone que assertEqual(resultado, None).
| Necesidad | Aserción recomendada |
|---|---|
| Comparar dos valores | assertEqual |
| Verificar verdadero o falso | assertTrue o assertFalse |
Verificar None |
assertIsNone |
| Verificar pertenencia | assertIn o assertNotIn |
| Verificar una excepción | assertRaises |
| Comparar decimales | assertAlmostEqual |
assertEqual: muchas veces hay una aserción más expresiva.assertAlmostEqual.mkdir aserciones-unittest-demo
cd aserciones-unittest-demo
python -m unittest
python -m unittest -v
python -m unittest test_usuarios.TestUsuarios.test_nombre_vacio_lanza_error
unittest ofrece muchas aserciones específicas.assertRaises sirve para validar errores esperados.assertAlmostEqual es útil para cálculos decimales.En este tema vimos las aserciones principales de unittest. Ya podemos comparar valores, verificar booleanos, comprobar pertenencia, validar None, probar excepciones y trabajar con números decimales.
En el próximo tema usaremos estas ideas para probar funciones puras y valores de retorno, uno de los casos más simples y frecuentes en Testing en Python.