En el tema anterior creamos un script para ejecutar la suite automatizada. Ahora vamos a centralizar la configuración de pytest para no repetir opciones en cada comando ni en cada script.
Veremos dos alternativas habituales: pytest.ini y pyproject.toml. Usaremos ejemplos concretos para configurar carpetas de prueba, nombres de archivos, opciones por defecto y marcadores.
Cuando un proyecto crece, empiezan a aparecer comandos largos y repetidos. Por ejemplo:
python -m pytest tests -v --strict-markers --tb=short
Si cada persona escribe el comando de memoria, es fácil que algunas pruebas se ejecuten con opciones distintas. La configuración centralizada evita esa diferencia.
La idea es que este comando simple:
python -m pytest
ya sepa dónde buscar pruebas y qué opciones aplicar.
pytest puede leer configuración desde varios archivos. Los más comunes son:
pytest.ini: simple, directo y muy usado en proyectos centrados en pruebas.pyproject.toml: archivo moderno para concentrar configuración de varias herramientas Python.tox.ini: frecuente en proyectos que usan tox.setup.cfg: usado en algunos proyectos antiguos.En este curso trabajaremos con pytest.ini y pyproject.toml, porque son las opciones más claras para esta etapa.
Si quieres una configuración simple, crea o reemplaza el archivo pytest.ini en la raíz del proyecto:
[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts = -v --strict-markers --tb=short
markers =
lento: pruebas que tardan más tiempo en ejecutarse
regresion: pruebas importantes para detectar regresiones
config: pruebas relacionadas con configuración del proyecto
Con esta configuración, pytest buscará pruebas dentro de tests, aplicará salida detallada y validará que los marcadores usados estén declarados.
Después de guardar pytest.ini, ejecuta:
python -m pytest
Aunque el comando es corto, pytest aplicará las opciones definidas en addopts. Por eso no hace falta escribir -v cada vez.
La opción testpaths indica dónde debe buscar pruebas pytest cuando ejecutamos la suite sin indicar una ruta específica.
testpaths = tests
Esto evita que pytest recorra carpetas innecesarias como .venv, reports u otros directorios del proyecto.
Estas opciones definen convenciones de nombres:
python_files = test_*.py
python_functions = test_*
Con esta configuración, pytest detectará archivos como test_calculadora.py y funciones como test_sumar_devuelve_la_suma.
Mantener una convención evita que una prueba no se ejecute por tener un nombre incorrecto.
addopts permite definir opciones que pytest usará automáticamente en cada ejecución.
addopts = -v --strict-markers --tb=short
-v: muestra salida detallada.--strict-markers: obliga a declarar los marcadores antes de usarlos.--tb=short: muestra trazas de error más compactas.Los marcadores permiten clasificar pruebas. En la configuración declaramos los que usaremos:
markers =
lento: pruebas que tardan más tiempo en ejecutarse
regresion: pruebas importantes para detectar regresiones
config: pruebas relacionadas con configuración del proyecto
Declararlos ayuda a documentar el significado de cada categoría y evita errores de escritura.
Modifica tests/test_config.py para marcar las pruebas de configuración:
import pytest
from app.config import obtener_ambiente, obtener_carpeta_reportes, obtener_timeout
@pytest.mark.config
def test_obtener_ambiente_desde_env():
assert obtener_ambiente() == "local"
@pytest.mark.config
def test_obtener_timeout_desde_env():
assert obtener_timeout() == 5
@pytest.mark.config
def test_obtener_carpeta_reportes_desde_env():
assert obtener_carpeta_reportes() == "reports"
Ahora esas pruebas pertenecen al grupo config.
Para ejecutar solo las pruebas de configuración:
python -m pytest -m config
Para excluir pruebas lentas:
python -m pytest -m "not lento"
Este criterio será muy útil cuando la suite tenga pruebas rápidas, lentas, críticas o de regresión.
Otra forma de configurar pytest es usar pyproject.toml. Este archivo permite concentrar configuración de distintas herramientas Python.
Si decides usar pyproject.toml, puedes escribir:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "-v --strict-markers --tb=short"
markers = [
"lento: pruebas que tardan más tiempo en ejecutarse",
"regresion: pruebas importantes para detectar regresiones",
"config: pruebas relacionadas con configuración del proyecto",
]
El contenido es equivalente al ejemplo con pytest.ini, pero usa formato TOML.
Para este curso, cualquiera de las dos alternativas funciona. Como regla práctica:
pytest.ini si quieres una configuración simple y exclusiva de pytest.pyproject.toml si el proyecto ya centraliza allí la configuración de herramientas Python.No conviene mantener la misma configuración en ambos archivos. Si duplicas opciones, será más difícil saber cuál está usando el proyecto.
Como pytest.ini ya define -v, podemos simplificar el comando base del script:
def construir_comando(args):
comando = [sys.executable, "-m", "pytest"]
if args.rapido:
comando.extend(["-m", "not lento"])
if args.detener:
comando.append("-x")
if args.reporte:
REPORTS_DIR.mkdir(exist_ok=True)
comando.extend([
"--html=reports/reporte.html",
"--self-contained-html",
])
return comando
El script queda más limpio porque las opciones generales viven en la configuración centralizada.
Para revisar qué configuración está tomando pytest, puedes ejecutar:
python -m pytest --trace-config
La salida es extensa, pero sirve para diagnosticar qué archivo de configuración fue detectado.
Si usas un marcador no declarado y tienes --strict-markers, pytest fallará. Por ejemplo:
import pytest
@pytest.mark.critica
def test_ejemplo():
assert True
Si critica no está declarado en pytest.ini, pytest informará el problema. Esto es bueno: evita que una prueba quede mal clasificada por un error de escritura.
Para permitir ese marcador, agrégalo a pytest.ini:
markers =
lento: pruebas que tardan más tiempo en ejecutarse
regresion: pruebas importantes para detectar regresiones
config: pruebas relacionadas con configuración del proyecto
critica: pruebas indispensables para validar el comportamiento principal
Luego puedes ejecutar:
python -m pytest -m critica
Para continuar el curso, deja este pytest.ini en la raíz:
[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts = -v --strict-markers --tb=short
markers =
lento: pruebas que tardan más tiempo en ejecutarse
regresion: pruebas importantes para detectar regresiones
config: pruebas relacionadas con configuración del proyecto
critica: pruebas indispensables para validar el comportamiento principal
markers.testpaths y las convenciones de nombres.pytest.ini o pyproject.toml, pero no ambos para lo mismo.addopts.Agrega un marcador llamado texto para identificar pruebas relacionadas con el módulo app/textos.py.
Luego marca las pruebas de tests/test_textos.py con @pytest.mark.texto y ejecuta solo ese grupo:
python -m pytest -m texto
En pytest.ini, agrega:
markers =
lento: pruebas que tardan más tiempo en ejecutarse
regresion: pruebas importantes para detectar regresiones
config: pruebas relacionadas con configuración del proyecto
critica: pruebas indispensables para validar el comportamiento principal
texto: pruebas relacionadas con normalización y manejo de textos
En tests/test_textos.py:
import pytest
from app.textos import normalizar_texto
@pytest.mark.texto
def test_normalizar_texto_quita_espacios_externos():
assert normalizar_texto(" Python ") == "python"
Finalmente ejecuta:
python -m pytest -m texto
Antes de continuar con el próximo tema, verifica lo siguiente:
pytest.ini o pyproject.toml, pero no ambos con la misma configuración.testpaths apunta a la carpeta tests.addopts contiene opciones generales de la suite.python -m pytest.run_tests.py no duplica opciones innecesarias.En este tema centralizamos la configuración de pytest. Esto permite ejecutar la suite con comandos simples y reglas consistentes para todos los alumnos o integrantes de un equipo.
También vimos cómo declarar marcadores, seleccionar grupos de pruebas y elegir entre pytest.ini y pyproject.toml. En el próximo tema trabajaremos con convenciones de nombres para archivos, carpetas, datos y utilidades.