Muchas aplicaciones leen y escriben archivos: configuraciones, reportes, exportaciones, logs o datos en formato JSON. Para probar ese código no conviene usar archivos reales del proyecto, porque una prueba podría modificar información importante o dejar archivos basura.
En este tema usaremos tmp_path, una fixture de pytest que crea una carpeta temporal para cada prueba.
Crea un proyecto nuevo:
mkdir pytest-archivos-demo
cd pytest-archivos-demo
Si pytest no está instalado en el entorno activo:
python -m pip install pytest
tmp_path es una fixture incorporada de pytest. Cuando la agregamos como parámetro de una prueba, pytest entrega una ruta temporal representada como objeto Path.
def test_ejemplo(tmp_path):
archivo = tmp_path / "datos.txt"
archivo.write_text("Hola", encoding="utf-8")
assert archivo.read_text(encoding="utf-8") == "Hola"
Cada prueba recibe su propia carpeta temporal. Eso ayuda a que las pruebas no se afecten entre sí.
Crea un archivo llamado archivos.py:
import json
from pathlib import Path
def guardar_texto(ruta, contenido):
ruta = Path(ruta)
ruta.write_text(contenido, encoding="utf-8")
def leer_texto(ruta):
ruta = Path(ruta)
return ruta.read_text(encoding="utf-8")
def agregar_linea(ruta, linea):
ruta = Path(ruta)
with ruta.open("a", encoding="utf-8") as archivo:
archivo.write(linea + "\n")
def contar_lineas(ruta):
contenido = leer_texto(ruta)
if contenido == "":
return 0
return len(contenido.splitlines())
def guardar_json(ruta, datos):
ruta = Path(ruta)
texto = json.dumps(datos, ensure_ascii=False, indent=2)
ruta.write_text(texto, encoding="utf-8")
def leer_json(ruta):
ruta = Path(ruta)
texto = ruta.read_text(encoding="utf-8")
return json.loads(texto)
def listar_archivos_txt(carpeta):
carpeta = Path(carpeta)
return sorted(archivo.name for archivo in carpeta.glob("*.txt"))
El módulo trabaja con Path para poder recibir rutas como texto o como objetos de pathlib.
Crea un archivo llamado test_archivos.py y agrega esta prueba:
from archivos import guardar_texto
def test_guardar_texto_crea_archivo(tmp_path):
ruta = tmp_path / "saludo.txt"
guardar_texto(ruta, "Hola Python")
assert ruta.exists()
assert ruta.read_text(encoding="utf-8") == "Hola Python"
La prueba verifica dos cosas: que el archivo exista y que su contenido sea correcto.
Para probar lectura, primero preparamos el archivo dentro de la carpeta temporal:
from archivos import leer_texto
def test_leer_texto_devuelve_contenido(tmp_path):
ruta = tmp_path / "mensaje.txt"
ruta.write_text("Contenido de prueba", encoding="utf-8")
resultado = leer_texto(ruta)
assert resultado == "Contenido de prueba"
La prueba no depende de un archivo creado manualmente. Ella misma prepara el escenario.
Para código que abre un archivo en modo agregado:
from archivos import agregar_linea
def test_agregar_linea_al_final_del_archivo(tmp_path):
ruta = tmp_path / "notas.txt"
ruta.write_text("Primera\n", encoding="utf-8")
agregar_linea(ruta, "Segunda")
assert ruta.read_text(encoding="utf-8") == "Primera\nSegunda\n"
Este tipo de prueba detecta errores frecuentes con saltos de línea.
Probemos una función que lee y procesa contenido:
from archivos import contar_lineas
def test_contar_lineas(tmp_path):
ruta = tmp_path / "items.txt"
ruta.write_text("uno\ndos\ntres\n", encoding="utf-8")
assert contar_lineas(ruta) == 3
También conviene cubrir el archivo vacío:
def test_contar_lineas_con_archivo_vacio(tmp_path):
ruta = tmp_path / "vacio.txt"
ruta.write_text("", encoding="utf-8")
assert contar_lineas(ruta) == 0
Si intentamos leer un archivo inexistente, Python debe lanzar FileNotFoundError:
import pytest
def test_leer_texto_con_archivo_inexistente_lanza_error(tmp_path):
ruta = tmp_path / "no_existe.txt"
with pytest.raises(FileNotFoundError):
leer_texto(ruta)
La prueba documenta el comportamiento esperado ante una ruta inválida.
Los archivos JSON son muy comunes en configuraciones y datos de intercambio:
from archivos import guardar_json, leer_json
def test_guardar_y_leer_json(tmp_path):
ruta = tmp_path / "producto.json"
datos = {
"nombre": "Teclado",
"precio": 50000,
"activo": True,
}
guardar_json(ruta, datos)
resultado = leer_json(ruta)
assert resultado == datos
La prueba valida el ciclo completo: guardar datos y volver a leerlos.
tmp_path también permite crear subcarpetas:
from archivos import listar_archivos_txt
def test_listar_archivos_txt(tmp_path):
carpeta = tmp_path / "reportes"
carpeta.mkdir()
(carpeta / "enero.txt").write_text("Enero", encoding="utf-8")
(carpeta / "febrero.txt").write_text("Febrero", encoding="utf-8")
(carpeta / "datos.csv").write_text("1,2,3", encoding="utf-8")
resultado = listar_archivos_txt(carpeta)
assert resultado == ["enero.txt", "febrero.txt"]
La función ignora el archivo datos.csv porque solo busca archivos con extensión .txt.
Si varias pruebas necesitan el mismo archivo, podemos preparar una fixture:
@pytest.fixture
def archivo_con_tareas(tmp_path):
ruta = tmp_path / "tareas.txt"
ruta.write_text("comprar\nestudiar\npracticar\n", encoding="utf-8")
return ruta
def test_contar_lineas_con_fixture(archivo_con_tareas):
assert contar_lineas(archivo_con_tareas) == 3
La fixture devuelve la ruta lista para usar.
El archivo test_archivos.py puede quedar así:
import pytest
from archivos import (
agregar_linea,
contar_lineas,
guardar_json,
guardar_texto,
leer_json,
leer_texto,
listar_archivos_txt,
)
def test_guardar_texto_crea_archivo(tmp_path):
ruta = tmp_path / "saludo.txt"
guardar_texto(ruta, "Hola Python")
assert ruta.exists()
assert ruta.read_text(encoding="utf-8") == "Hola Python"
def test_leer_texto_devuelve_contenido(tmp_path):
ruta = tmp_path / "mensaje.txt"
ruta.write_text("Contenido de prueba", encoding="utf-8")
resultado = leer_texto(ruta)
assert resultado == "Contenido de prueba"
def test_agregar_linea_al_final_del_archivo(tmp_path):
ruta = tmp_path / "notas.txt"
ruta.write_text("Primera\n", encoding="utf-8")
agregar_linea(ruta, "Segunda")
assert ruta.read_text(encoding="utf-8") == "Primera\nSegunda\n"
def test_contar_lineas(tmp_path):
ruta = tmp_path / "items.txt"
ruta.write_text("uno\ndos\ntres\n", encoding="utf-8")
assert contar_lineas(ruta) == 3
def test_contar_lineas_con_archivo_vacio(tmp_path):
ruta = tmp_path / "vacio.txt"
ruta.write_text("", encoding="utf-8")
assert contar_lineas(ruta) == 0
def test_leer_texto_con_archivo_inexistente_lanza_error(tmp_path):
ruta = tmp_path / "no_existe.txt"
with pytest.raises(FileNotFoundError):
leer_texto(ruta)
def test_guardar_y_leer_json(tmp_path):
ruta = tmp_path / "producto.json"
datos = {
"nombre": "Teclado",
"precio": 50000,
"activo": True,
}
guardar_json(ruta, datos)
resultado = leer_json(ruta)
assert resultado == datos
def test_listar_archivos_txt(tmp_path):
carpeta = tmp_path / "reportes"
carpeta.mkdir()
(carpeta / "enero.txt").write_text("Enero", encoding="utf-8")
(carpeta / "febrero.txt").write_text("Febrero", encoding="utf-8")
(carpeta / "datos.csv").write_text("1,2,3", encoding="utf-8")
resultado = listar_archivos_txt(carpeta)
assert resultado == ["enero.txt", "febrero.txt"]
@pytest.fixture
def archivo_con_tareas(tmp_path):
ruta = tmp_path / "tareas.txt"
ruta.write_text("comprar\nestudiar\npracticar\n", encoding="utf-8")
return ruta
def test_contar_lineas_con_fixture(archivo_con_tareas):
assert contar_lineas(archivo_con_tareas) == 3
Desde la raíz del proyecto, ejecuta:
python -m pytest
La salida esperada será similar a:
collected 9 items
test_archivos.py ......... [100%]
9 passed in 0.05s
Mientras aprendes, puedes imprimir la ruta temporal para entender dónde trabaja pytest:
def test_mostrar_tmp_path(tmp_path):
print(tmp_path)
assert tmp_path.exists()
Ejecuta con -s para ver la salida de print:
python -m pytest -s
En pruebas reales evita dejar print innecesarios.
Evita pruebas que dependan de rutas fijas como C:\datos\archivo.txt o de archivos creados manualmente. Esas pruebas suelen fallar en otra computadora o cuando cambia la ubicación del proyecto.
encoding="utf-8" al leer y escribir texto.sorted.mkdir pytest-archivos-demo
cd pytest-archivos-demo
python -m pip install pytest
python -m pytest
python -m pytest -v
python -m pytest -s
python -m pytest test_archivos.py::test_guardar_y_leer_json -v
tmp_path crea una carpeta temporal por prueba.Path facilita construir rutas portables.En este tema probamos código que lee y escribe archivos usando carpetas temporales. Con tmp_path, cada prueba puede crear sus propios archivos sin afectar el proyecto real.
En el próximo tema veremos cómo probar entrada y salida por consola, incluyendo texto impreso con print y datos ingresados por el usuario.