Muchas suites automatizadas necesitan comportarse distinto según el ambiente: local, pruebas, staging o producción. Para eso se usan variables de entorno y archivos de configuración.
En este tema veremos cómo leer variables de entorno, cargar valores desde .env, convertir tipos y probar configuración con monkeypatch sin depender del ambiente real de la computadora.
Una variable de entorno es un valor disponible para un proceso. En Python se puede leer con os.getenv.
import os
ambiente = os.getenv("APP_ENV", "local")
El segundo argumento es el valor por defecto si la variable no existe.
En la raíz del proyecto, crea o ajusta el archivo .env:
APP_ENV=local
TEST_TIMEOUT=5
REPORTS_DIR=reports
FEATURE_CUPONES=true
python-dotenv permite cargar esos valores como variables de entorno durante la ejecución local.
Crea o actualiza app/config.py:
import os
from dotenv import load_dotenv
load_dotenv()
def obtener_ambiente():
return os.getenv("APP_ENV", "local")
def obtener_timeout():
return int(os.getenv("TEST_TIMEOUT", "5"))
def obtener_carpeta_reportes():
return os.getenv("REPORTS_DIR", "reports")
def cupones_habilitados():
return os.getenv("FEATURE_CUPONES", "false").lower() == "true"
Este módulo centraliza la lectura de configuración. Las pruebas y scripts no deberían leer todas las variables por su cuenta.
Crea tests/test_config_ambiente.py:
from app.config import (
cupones_habilitados,
obtener_ambiente,
obtener_carpeta_reportes,
obtener_timeout,
)
def test_configuracion_lee_ambiente_local():
assert obtener_ambiente() == "local"
def test_configuracion_lee_timeout_como_entero():
assert obtener_timeout() == 5
def test_configuracion_lee_carpeta_reportes():
assert obtener_carpeta_reportes() == "reports"
def test_configuracion_lee_feature_cupones():
assert cupones_habilitados() is True
Ejecuta:
python -m pytest tests/test_config_ambiente.py
Las pruebas anteriores dependen de que el archivo .env tenga valores específicos. Si otro alumno cambia el archivo, las pruebas pueden fallar aunque el código esté bien.
Para probar configuración de forma controlada, conviene usar monkeypatch y definir variables dentro de cada prueba.
monkeypatch.setenv permite definir una variable de entorno durante una prueba:
from app.config import obtener_ambiente
def test_obtener_ambiente_desde_variable(monkeypatch):
monkeypatch.setenv("APP_ENV", "testing")
assert obtener_ambiente() == "testing"
La variable se restaura automáticamente al terminar la prueba.
Para probar valores por defecto, podemos eliminar una variable durante la prueba:
from app.config import obtener_ambiente
def test_obtener_ambiente_sin_variable_devuelve_local(monkeypatch):
monkeypatch.delenv("APP_ENV", raising=False)
assert obtener_ambiente() == "local"
raising=False evita error si la variable no existe.
Las variables de entorno siempre llegan como texto. Si necesitamos números, hay que convertirlos.
from app.config import obtener_timeout
def test_obtener_timeout_convierte_variable_a_entero(monkeypatch):
monkeypatch.setenv("TEST_TIMEOUT", "10")
assert obtener_timeout() == 10
La prueba verifica que el módulo de configuración convierta correctamente el valor.
Para valores booleanos, conviene definir reglas claras. En nuestro caso, solo true habilita la funcionalidad.
from app.config import cupones_habilitados
def test_cupones_habilitados_con_true_devuelve_true(monkeypatch):
monkeypatch.setenv("FEATURE_CUPONES", "true")
assert cupones_habilitados() is True
def test_cupones_habilitados_con_false_devuelve_false(monkeypatch):
monkeypatch.setenv("FEATURE_CUPONES", "false")
assert cupones_habilitados() is False
Podemos agregar una función para saber si estamos en ambiente local:
def es_ambiente_local():
return obtener_ambiente() == "local"
Prueba:
from app.config import es_ambiente_local
def test_es_ambiente_local_con_local_devuelve_true(monkeypatch):
monkeypatch.setenv("APP_ENV", "local")
assert es_ambiente_local() is True
def test_es_ambiente_local_con_testing_devuelve_false(monkeypatch):
monkeypatch.setenv("APP_ENV", "testing")
assert es_ambiente_local() is False
La configuración por ambiente se presta bien para parametrización:
import pytest
from app.config import es_ambiente_local
@pytest.mark.parametrize("ambiente, esperado", [
("local", True),
("testing", False),
("staging", False),
])
def test_es_ambiente_local_devuelve_resultado_esperado(monkeypatch, ambiente, esperado):
monkeypatch.setenv("APP_ENV", ambiente)
assert es_ambiente_local() is esperado
No guardes contraseñas, tokens ni claves reales en archivos de prueba. Si una función necesita una clave, usa valores falsos controlados.
def test_api_key_se_lee_desde_variable(monkeypatch):
monkeypatch.setenv("API_KEY", "clave-falsa-para-pruebas")
assert obtener_api_key() == "clave-falsa-para-pruebas"
El objetivo es verificar la lectura de configuración, no usar credenciales reales.
Agrega en app/config.py:
def obtener_api_key():
return os.getenv("API_KEY", "")
Y prueba:
from app.config import obtener_api_key
def test_obtener_api_key_devuelve_variable_configurada(monkeypatch):
monkeypatch.setenv("API_KEY", "clave-falsa")
assert obtener_api_key() == "clave-falsa"
Podemos devolver una configuración distinta según APP_ENV:
def obtener_configuracion():
ambiente = obtener_ambiente()
configuraciones = {
"local": {"debug": True, "base_url": "http://localhost:8000"},
"testing": {"debug": False, "base_url": "http://testing.local"},
"staging": {"debug": False, "base_url": "https://staging.example.com"},
}
return configuraciones.get(ambiente, configuraciones["local"])
Esta función evita que el resto del proyecto conozca los detalles de cada ambiente.
Prueba la función anterior:
from app.config import obtener_configuracion
def test_obtener_configuracion_para_testing(monkeypatch):
monkeypatch.setenv("APP_ENV", "testing")
config = obtener_configuracion()
assert config["debug"] is False
assert config["base_url"] == "http://testing.local"
Si varias pruebas necesitan el mismo ambiente, puedes crear una fixture:
import pytest
@pytest.fixture
def ambiente_testing(monkeypatch):
monkeypatch.setenv("APP_ENV", "testing")
Uso:
def test_obtener_configuracion_con_fixture_testing(ambiente_testing):
config = obtener_configuracion()
assert config["base_url"] == "http://testing.local"
Usa .env para ejecutar el proyecto localmente con una configuración cómoda. Usa monkeypatch para pruebas automatizadas que necesitan controlar variables sin depender del entorno real.
.env: configuración local del proyecto.monkeypatch.setenv: definir valores específicos durante una prueba.monkeypatch.delenv: probar comportamiento cuando falta una variable.int o float.load_dotenv() se ejecute y que el archivo esté en la raíz esperada..env del repositorio.Agrega en app/config.py una función obtener_max_reintentos. Debe leer MAX_REINTENTOS y devolver un entero. Si la variable no existe, debe devolver 3.
Luego crea pruebas para:
5.3.0.Función:
def obtener_max_reintentos():
return int(os.getenv("MAX_REINTENTOS", "3"))
Pruebas:
from app.config import obtener_max_reintentos
def test_obtener_max_reintentos_con_variable_definida(monkeypatch):
monkeypatch.setenv("MAX_REINTENTOS", "5")
assert obtener_max_reintentos() == 5
def test_obtener_max_reintentos_sin_variable_devuelve_tres(monkeypatch):
monkeypatch.delenv("MAX_REINTENTOS", raising=False)
assert obtener_max_reintentos() == 3
def test_obtener_max_reintentos_con_cero_devuelve_cero(monkeypatch):
monkeypatch.setenv("MAX_REINTENTOS", "0")
assert obtener_max_reintentos() == 0
Ejecuta:
python -m pytest tests/test_config_ambiente.py
Antes de continuar con el próximo tema, verifica lo siguiente:
app/config.py.monkeypatch.setenv para definir variables en pruebas.monkeypatch.delenv para probar variables ausentes.python -m pytest.En este tema trabajamos con variables de entorno y configuración por ambiente. Aprendimos a evitar pruebas dependientes del entorno real usando monkeypatch para controlar cada caso.
En el próximo tema automatizaremos verificaciones de salida, logs y mensajes de error, otro aspecto clave para diagnosticar fallas en suites automatizadas.