Siempre haz mock con autospec=True
Tus mocks aceptan métodos que no existen. Un Mock plano (o MagicMock) es infinitamente complaciente: llama a mock.fetch_userz(), a mock.send(1, 2, 3, 4, 5) o a cualquier método mal escrito, renombrado o inventado, y devuelve alegremente otro mock. El test queda en verde. En una base de código asistida por IA — donde un agente puede renombrar un método, adivinar un parámetro o inventar una API — esa complacencia convierte tu suite de tests en un sello de goma.
Por qué un Mock plano te miente
Mock crea atributos automáticamente al accederlos. Esa es la característica que hace cómodo el mocking, y también su modo de fallo: nada ata el mock a la interfaz del objeto real. En concreto, dos cosas quedan sin verificar:
- Atributos inexistentes. Renombra
Client.get_useraClient.fetch_useren el código de producción. Un test que parcheaClientcon un mock plano y llama aclient.get_user()sigue pasando — el mock inventaget_useren el momento. - Firmas incorrectas. Agrega un argumento obligatorio a una función real. Los tests que llaman a la versión mockeada con la aridad vieja siguen pasando, porque un mock plano acepta cualquier llamada.
Ambos son exactamente los errores más frecuentes de los asistentes de código con IA: nombres de métodos plausibles pero incorrectos y firmas de llamada ligeramente desviadas.
La solución: autospec=True
Pasa autospec=True a patch() y el mock se construye a partir del spec del objeto real — de forma recursiva. Según la documentación de Python: "All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced", y las funciones mockeadas "will have their arguments checked and will raise a TypeError if they are called with the wrong signature".
from unittest.mock import patch
# Rubber stamp: passes even if get_user() was renamed
with patch("app.client.Client") as mock_client:
mock_client.return_value.get_userz() # green, silently wrong
# Reality check: fails the moment the API drifts
with patch("app.client.Client", autospec=True) as mock_client:
mock_client.return_value.get_userz() # AttributeError
mock_client.return_value.get_user("a", "b") # TypeError if the real
# signature is get_user(id)
Acceder a un atributo que el objeto real no tiene lanza AttributeError; llamar con los argumentos equivocados lanza TypeError — igual que fallaría el código de producción. Tus tests ahora atrapan el mal uso de una API por parte de la IA en lugar de esconderlo silenciosamente.
Reglas prácticas
| Situación | Usa |
|---|---|
patch() sobre una clase, función o atributo de módulo | patch(target, autospec=True) — el hábito por defecto |
| Construir un mock con spec a mano (sin parchear) | create_autospec(RealThing) |
| Prohibir también asignar atributos que no existen | spec_set=True (variante más estricta de spec) |
| El mock debe actuar como instancia, no como la clase | create_autospec(RealThing, instance=True) |
| Mocks planos heredados que aún no puedes reescribir | como mínimo patch(target, spec=True) |
spec=True verifica el acceso a atributos; spec_set=True además lanza AttributeError cuando un test asigna un atributo que el objeto real no tiene — lo que atrapa errores de tipeo en la propia preparación del test.
Notas para usuarios avanzados
- Aserciones conscientes de la firma. Un mock con spec introspecciona la firma real, así que
assert_called_with()empareja las formas posicional y por nombre de la misma llamada —mock.get_user(1)ymock.get_user(id=1)comparan como iguales. Los mocks planos las tratan como llamadas distintas. - Las clases dan spec a sus instancias. Con
autospec=Truesobre una clase, elreturn_valuedel mock (la "instancia") lleva el mismo spec, así que toda la cadena queda verificada. - Limitación conocida: atributos creados en
__init__. Autospec introspecciona la clase, por lo que los atributos creados solo dentro de__init__(p. ej.self.session = ...) le resultan invisibles y lanzaránAttributeErroren los tests. Asígnalos explícitamente en el mock, o exponlos a nivel de clase. - Costo. Autospec construye el spec de forma recursiva mediante introspección, así que es más lento que un
Mockpelado — despreciable en la mayoría de las suites, pero vale saberlo en fixtures muy calientes. - Hazlo el estándar en code review. Un lint barato y de alto rendimiento para tests generados por IA: marca cualquier
patch(sinautospec=True(o unspecexplícito). Es un solo argumento, y convierte "los tests pasan" de nuevo en evidencia.
Recursos
- Autospeccing — documentación de unittest.mock
- patch() — documentación de unittest.mock
- create_autospec() — documentación de unittest.mock
- spec y spec_set — referencia de la clase Mock
- unittest.mock — ejemplos introductorios
¿Construyendo una funcionalidad con IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.