Una prueba automatizada debe dejar el entorno en buen estado. Si una prueba crea archivos, modifica variables o prepara recursos, debe evitar afectar a las pruebas siguientes.
En este tema veremos técnicas para limpiar datos, archivos y recursos después de cada prueba usando fixtures, yield, tmp_path y herramientas de pytest.
Si una prueba deja residuos, otra prueba puede fallar o pasar por una razón incorrecta. Esto genera suites frágiles y difíciles de diagnosticar.
Problemas típicos:
Cuando una prueba necesita crear archivos, la primera opción debería ser tmp_path:
def test_crea_archivo_temporal(tmp_path):
ruta = tmp_path / "salida.txt"
ruta.write_text("contenido", encoding="utf-8")
assert ruta.exists()
pytest se encarga de manejar esa carpeta temporal sin ensuciar el proyecto.
Evita escribir directamente en archivos compartidos como:
from pathlib import Path
def test_mala_practica():
Path("salida.txt").write_text("contenido", encoding="utf-8")
Ese archivo queda en la raíz del proyecto y puede afectar otras pruebas o ejecuciones posteriores.
Cuando una fixture prepara un recurso que debe limpiarse, usa yield:
import pytest
@pytest.fixture
def recurso_temporal():
recurso = {"abierto": True}
yield recurso
recurso["abierto"] = False
El código posterior a yield se ejecuta al finalizar la prueba.
Podemos crear una carpeta temporal preparada:
import pytest
@pytest.fixture
def carpeta_trabajo(tmp_path):
entrada = tmp_path / "entrada"
salida = tmp_path / "salida"
entrada.mkdir()
salida.mkdir()
return {
"entrada": entrada,
"salida": salida,
}
Como usa tmp_path, no necesita limpieza manual.
Ejemplo de uso:
def test_crea_archivo_en_carpeta_salida(carpeta_trabajo):
ruta = carpeta_trabajo["salida"] / "resultado.txt"
ruta.write_text("ok", encoding="utf-8")
assert ruta.read_text(encoding="utf-8") == "ok"
La prueba no escribe en carpetas reales del proyecto.
Para variables de entorno, usa monkeypatch. pytest restaura los cambios automáticamente.
def test_configuracion_temporal(monkeypatch):
monkeypatch.setenv("APP_ENV", "testing")
assert obtener_ambiente() == "testing"
No necesitas limpiar manualmente APP_ENV después de la prueba.
Si tienes un recurso global, es mejor evitarlo. Pero si existe, usa una fixture para restaurarlo.
EVENTOS = []
def registrar_evento(evento):
EVENTOS.append(evento)
Fixture de limpieza:
import pytest
@pytest.fixture
def eventos_limpios():
EVENTOS.clear()
yield EVENTOS
EVENTOS.clear()
Uso de la fixture:
def test_registrar_evento_agrega_evento(eventos_limpios):
registrar_evento("usuario_creado")
assert eventos_limpios == ["usuario_creado"]
La lista se limpia antes y después de la prueba.
Si abres un archivo manualmente, ciérralo. Mejor aún, usa with:
def test_escritura_con_context_manager(tmp_path):
ruta = tmp_path / "datos.txt"
with ruta.open("w", encoding="utf-8") as archivo:
archivo.write("contenido")
assert ruta.read_text(encoding="utf-8") == "contenido"
El context manager cierra el archivo automáticamente.
En código de aplicación o scripts, try/finally permite limpiar aunque ocurra un error:
def usar_recurso(recurso):
try:
recurso["abierto"] = True
return "ok"
finally:
recurso["abierto"] = False
En pruebas, las fixtures con yield suelen ser más cómodas.
Los reportes son artefactos generados. Puedes limpiarlos antes de una ejecución usando el script creado en temas anteriores:
python scripts/limpiar_reportes.py
No limpies reportes si necesitas conservar evidencia de una ejecución anterior.
Una fixture autouse puede limpiar algo antes y después de cada prueba:
import pytest
@pytest.fixture(autouse=True)
def limpiar_eventos():
EVENTOS.clear()
yield
EVENTOS.clear()
Úsala con cuidado. Si la limpieza no es evidente, puede confundir a quien lee la prueba.
Evita autouse=True cuando la limpieza afecta solo a algunas pruebas. En ese caso, es más claro pedir la fixture explícitamente:
def test_registrar_evento_agrega_evento(eventos_limpios):
registrar_evento("usuario_creado")
assert eventos_limpios == ["usuario_creado"]
La limpieza es todavía más importante cuando ejecutamos pruebas en paralelo. Si dos pruebas escriben el mismo archivo, pueden fallar de forma intermitente.
Buenas prácticas para paralelo:
tmp_path.Puedes crear una prueba o script que verifique que no hay archivos inesperados en la raíz. Por ejemplo:
from pathlib import Path
def test_no_existe_salida_txt_en_raiz():
assert not Path("salida.txt").exists()
Este tipo de prueba puede ayudar a detectar prácticas incorrectas mientras se aprende.
Una prueba no debería borrar carpetas del proyecto, código fuente, archivos de configuración ni datos versionados.
La limpieza debe limitarse a recursos creados por la propia prueba o a carpetas claramente generadas, como reports cuando se ejecuta un script de limpieza.
tmp_path.monkeypatch.Crea app/eventos.py con una lista global EVENTOS y una función registrar_evento. Luego crea una fixture que limpie la lista antes y después de cada prueba que la use.
Automatiza estas pruebas:
Archivo app/eventos.py:
EVENTOS = []
def registrar_evento(evento):
EVENTOS.append(evento)
Pruebas:
import pytest
from app.eventos import EVENTOS, registrar_evento
@pytest.fixture
def eventos_limpios():
EVENTOS.clear()
yield EVENTOS
EVENTOS.clear()
def test_registrar_evento_agrega_elemento(eventos_limpios):
registrar_evento("usuario_creado")
assert eventos_limpios == ["usuario_creado"]
def test_registrar_dos_eventos_conserva_orden(eventos_limpios):
registrar_evento("usuario_creado")
registrar_evento("email_enviado")
assert eventos_limpios == ["usuario_creado", "email_enviado"]
def test_eventos_limpios_comienza_vacio(eventos_limpios):
assert eventos_limpios == []
Ejecuta:
python -m pytest tests/test_eventos.py
Antes de continuar con el próximo tema, verifica lo siguiente:
tmp_path.monkeypatch.python -m pytest.En este tema vimos cómo limpiar datos, archivos y recursos después de cada prueba. La limpieza correcta mantiene la suite aislada, repetible y preparada para ejecución completa o paralela.
En el próximo tema trabajaremos con diagnóstico de fallas en suites automatizadas.