En el tema anterior escribimos las primeras pruebas con unittest. Ahora nos concentraremos en la ejecución: cómo correr todas las pruebas, una carpeta, un archivo, una clase o un método específico.
Esto es importante porque en un proyecto real no siempre queremos ejecutar todo. A veces necesitamos correr una sola prueba mientras corregimos un error, y luego ejecutar la suite completa antes de terminar.
El comando python -m unittest ejecuta el módulo unittest usando el mismo intérprete de Python que está activo en la terminal.
Esto evita confusiones cuando hay varias versiones de Python instaladas. También funciona sin instalar paquetes externos, porque unittest forma parte de la biblioteca estándar.
Crea un proyecto nuevo para este tema:
mkdir ejecutar-unittest-demo
cd ejecutar-unittest-demo
Crearemos una estructura con código principal y pruebas separadas.
Crea dos carpetas:
mkdir app
mkdir tests
Agrega archivos __init__.py para trabajar con paquetes. En Windows PowerShell:
New-Item app\__init__.py -ItemType File
New-Item tests\__init__.py -ItemType File
En Linux o macOS:
touch app/__init__.py
touch tests/__init__.py
Dentro de app, crea numeros.py:
def sumar(a, b):
return a + b
def dividir(a, b):
if b == 0:
raise ValueError("No se puede dividir por cero")
return a / b
def es_mayor_de_edad(edad):
return edad >= 18
Este archivo tiene funciones simples para practicar distintas formas de ejecución.
Dentro de tests, crea test_numeros.py:
import unittest
from app.numeros import dividir, es_mayor_de_edad, sumar
class TestNumeros(unittest.TestCase):
def test_sumar_dos_numeros(self):
self.assertEqual(sumar(2, 3), 5)
def test_dividir_dos_numeros(self):
self.assertEqual(dividir(10, 2), 5)
def test_dividir_por_cero_lanza_error(self):
with self.assertRaises(ValueError):
dividir(10, 0)
def test_edad_18_es_mayor_de_edad(self):
self.assertTrue(es_mayor_de_edad(18))
Dentro de app, crea textos.py:
def normalizar(texto):
return texto.strip().lower()
def contar_palabras(texto):
texto = texto.strip()
if not texto:
return 0
return len(texto.split())
Luego crea tests/test_textos.py:
import unittest
from app.textos import contar_palabras, normalizar
class TestTextos(unittest.TestCase):
def test_normalizar_texto(self):
self.assertEqual(normalizar(" Hola "), "hola")
def test_contar_palabras(self):
self.assertEqual(contar_palabras("uno dos tres"), 3)
def test_contar_palabras_en_texto_vacio(self):
self.assertEqual(contar_palabras(" "), 0)
La estructura queda así:
ejecutar-unittest-demo/
|-- app/
| |-- __init__.py
| |-- numeros.py
| `-- textos.py
`-- tests/
|-- __init__.py
|-- test_numeros.py
`-- test_textos.py
Desde la raíz del proyecto, ejecuta:
python -m unittest
Salida esperada:
.......
----------------------------------------------------------------------
Ran 7 tests in 0.001s
OK
Este comando descubre pruebas en archivos cuyo nombre coincide con el patrón esperado por unittest.
La forma explícita de pedir descubrimiento automático es:
python -m unittest discover
Este comando busca pruebas desde la carpeta actual. En proyectos simples suele producir el mismo resultado que python -m unittest.
Para indicar la carpeta donde están las pruebas:
python -m unittest discover -s tests
La opción -s significa start directory, es decir, carpeta desde donde comienza la búsqueda.
Por defecto, unittest busca archivos con patrón test*.py. Podemos indicarlo explícitamente:
python -m unittest discover -s tests -p "test_*.py"
La opción -p define el patrón de nombres de archivo. En este curso usaremos archivos que comienzan con test_.
Para correr solo las pruebas de números:
python -m unittest tests.test_numeros
Observa que usamos puntos en lugar de barras y no escribimos la extensión .py.
Para ejecutar una clase específica:
python -m unittest tests.test_numeros.TestNumeros
El formato es paquete.archivo.Clase.
Para correr una sola prueba:
python -m unittest tests.test_numeros.TestNumeros.test_dividir_por_cero_lanza_error
Este formato es muy útil mientras corregimos un comportamiento puntual.
La opción -v muestra el nombre de cada prueba:
python -m unittest -v
También puede combinarse con ejecución selectiva:
python -m unittest tests.test_textos -v
La opción -f detiene la ejecución cuando ocurre la primera falla o error:
python -m unittest -f
Puede ser útil cuando la suite es grande y queremos corregir un problema por vez.
La opción -b captura la salida estándar durante las pruebas. Si una prueba pasa, no muestra los print. Si falla, muestra la salida relacionada:
python -m unittest -b
Esto ayuda a mantener limpia la terminal cuando hay mensajes impresos durante las pruebas.
Podemos combinar opciones:
python -m unittest discover -s tests -p "test_*.py" -v
Este comando busca pruebas en tests, usa el patrón test_*.py y muestra salida detallada.
Si unittest no encuentra pruebas, puede mostrar:
Ran 0 tests in 0.000s
OK
Esto no significa que el proyecto esté bien probado. Significa que no encontró pruebas para ejecutar. Revisa nombres de archivos, nombres de métodos, carpeta actual y comando usado.
Para evitar problemas, usa estas convenciones:
test_algo.py.Test.test_.tests.test_numeros, no tests/test_numeros.py, cuando ejecutas por módulo.__init__.py: puede causar problemas al importar módulos como paquetes.app o tests.test_: no serán descubiertos como pruebas.Ran 0 tests es una señal de problema.python -m unittest
python -m unittest discover
python -m unittest discover -s tests
python -m unittest discover -s tests -p "test_*.py"
python -m unittest tests.test_numeros
python -m unittest tests.test_numeros.TestNumeros
python -m unittest tests.test_numeros.TestNumeros.test_dividir_por_cero_lanza_error
python -m unittest -v
python -m unittest tests.test_textos -v
python -m unittest -f
python -m unittest -b
python -m unittest discover -s tests -p "test_*.py" -v
python -m unittest permite ejecutar pruebas sin instalar herramientas externas.discover busca pruebas automáticamente.-s indica la carpeta inicial de búsqueda.-p define el patrón de archivos de prueba.Ran 0 tests no es una buena señal: significa que no se descubrieron pruebas.En este tema aprendimos a ejecutar pruebas con python -m unittest de varias formas. Esto permite trabajar con precisión: correr una prueba específica mientras desarrollamos y ejecutar toda la suite antes de cerrar un cambio.
En el próximo tema estudiaremos las aserciones principales de unittest, que son las herramientas que usamos para expresar qué resultado esperamos en cada prueba.