Hasta ahora reemplazamos dependencias pasándolas como parámetros o constructores. Esa suele ser la opción más clara. Sin embargo, a veces el código ya importa o crea una dependencia internamente, y no podemos cambiarlo de inmediato.
En esos casos podemos usar patch, una herramienta de unittest.mock que reemplaza temporalmente un nombre durante una prueba.
patch forma parte de unittest.mock:
from unittest.mock import patch
También es habitual importarlo junto con Mock:
from unittest.mock import Mock, patch
patch reemplaza un nombre en un módulo durante un bloque de código, una función de prueba o una fixture. Al terminar, restaura el valor original.
Por ejemplo, puede reemplazar:
El reemplazo es temporal y controlado por la prueba.
Supongamos un archivo tienda/codigos.py:
from uuid import uuid4
def generar_codigo_pedido():
return f"PED-{uuid4()}"
La función usa uuid4, que genera un valor diferente en cada ejecución. Podemos reemplazarlo durante la prueba.
Usando patch con with:
from unittest.mock import patch
from tienda.codigos import generar_codigo_pedido
def test_generar_codigo_pedido():
with patch("tienda.codigos.uuid4") as uuid4_mock:
uuid4_mock.return_value = "ABC123"
codigo = generar_codigo_pedido()
assert codigo == "PED-ABC123"
Durante el bloque with, el nombre uuid4 dentro de tienda.codigos queda reemplazado por un mock.
El argumento de patch es una cadena con la ruta del nombre que queremos reemplazar:
patch("tienda.codigos.uuid4")
No estamos reemplazando uuid.uuid4 globalmente. Estamos reemplazando el nombre uuid4 que el módulo tienda.codigos usa.
Archivo tienda/facturas.py:
from datetime import date
def crear_numero_factura(cliente_id):
hoy = date.today()
return f"FAC-{hoy.year}-{cliente_id}"
Queremos controlar la fecha para que la prueba no dependa del día actual.
Podemos reemplazar date en el módulo donde se usa:
from datetime import date
from unittest.mock import patch
from tienda.facturas import crear_numero_factura
def test_crear_numero_factura():
with patch("tienda.facturas.date") as date_mock:
date_mock.today.return_value = date(2026, 5, 15)
numero = crear_numero_factura("CLI-10")
assert numero == "FAC-2026-CLI-10"
El código de producción sigue usando date.today(), pero la prueba controla qué fecha devuelve.
Supongamos este código:
from tienda.email import ServicioEmail
def enviar_bienvenida(usuario):
email = ServicioEmail()
email.enviar(
destino=usuario["email"],
asunto="Bienvenido",
)
La función crea internamente ServicioEmail. Si no podemos refactorizar todavía, usamos patch.
Cuando parcheamos una clase, el mock reemplaza la clase. Su return_value representa la instancia creada al llamar la clase.
from unittest.mock import patch
from tienda.notificaciones import enviar_bienvenida
def test_enviar_bienvenida():
usuario = {"email": "ana@example.com"}
with patch("tienda.notificaciones.ServicioEmail") as ServicioEmailMock:
instancia_email = ServicioEmailMock.return_value
enviar_bienvenida(usuario)
ServicioEmailMock.assert_called_once_with()
instancia_email.enviar.assert_called_once_with(
destino="ana@example.com",
asunto="Bienvenido",
)
Este patrón es muy común cuando el código instancia una clase dentro de la función probada.
No siempre queremos que patch cree un mock automático. Podemos indicar el reemplazo con new:
def funcion_falsa():
return "valor controlado"
with patch("modulo.funcion_original", new=funcion_falsa):
resultado = codigo_que_usa_funcion_original()
Esto sirve para reemplazar una función por una implementación simple.
Archivo tienda/configuracion.py:
MODO_DEBUG = False
def mostrar_detalle_error():
return MODO_DEBUG
Prueba:
from unittest.mock import patch
from tienda.configuracion import mostrar_detalle_error
def test_mostrar_detalle_error_en_debug():
with patch("tienda.configuracion.MODO_DEBUG", new=True):
assert mostrar_detalle_error() is True
La constante cambia solo dentro del bloque with.
patch.object permite reemplazar un atributo de un objeto o clase ya importado:
from unittest.mock import patch
with patch.object(objeto, "metodo") as metodo_mock:
metodo_mock.return_value = "ok"
Puede ser útil cuando tenemos una referencia directa al objeto y queremos reemplazar uno de sus atributos.
Supongamos una clase:
class CalculadoraImpuestos:
def obtener_tasa(self):
return 0.21
def calcular_iva(self, total):
return total * self.obtener_tasa()
Podemos reemplazar el método obtener_tasa para una prueba:
def test_calcular_iva_con_tasa_controlada():
calculadora = CalculadoraImpuestos()
with patch.object(calculadora, "obtener_tasa", return_value=0.10):
iva = calculadora.calcular_iva(1000)
assert iva == 100
Esta técnica debe usarse con criterio, porque parchear métodos del mismo objeto puede acoplar la prueba a detalles internos.
patch suele ser útil cuando:
Si puedes pasar la dependencia explícitamente sin complicar el diseño, la inyección de dependencias suele ser más clara.
Evita usar patch como primera opción si el código puede recibir sus dependencias por parámetro o constructor. Muchos parches en una prueba suelen indicar acoplamiento excesivo.
También conviene evitar parches sobre detalles internos que no forman parte del comportamiento que queremos verificar.
Prueba esta función reemplazando uuid4 con patch:
from uuid import uuid4
def crear_id_sesion(usuario_id):
return f"SES-{usuario_id}-{uuid4()}"
La prueba debe verificar que el identificador generado sea predecible.
Si la función está en seguridad/sesiones.py, la prueba puede ser:
from unittest.mock import patch
from seguridad.sesiones import crear_id_sesion
def test_crear_id_sesion():
with patch("seguridad.sesiones.uuid4") as uuid4_mock:
uuid4_mock.return_value = "ABC123"
sesion_id = crear_id_sesion("USR-1")
assert sesion_id == "SES-USR-1-ABC123"
uuid4_mock.assert_called_once_with()
El punto importante es parchear seguridad.sesiones.uuid4, porque ese es el nombre que usa el código bajo prueba.
patch permite reemplazar temporalmente funciones, clases, constantes y objetos durante una prueba. Es especialmente útil para controlar tiempo, aleatoriedad, identificadores y dependencias creadas internamente.
En el próximo tema veremos con más detalle el punto que más errores genera: dónde aplicar patch.