Yeda AI Tips · #081

English

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:

  1. Atributos inexistentes. Renombra Client.get_user a Client.fetch_user en el código de producción. Un test que parchea Client con un mock plano y llama a client.get_user() sigue pasando — el mock inventa get_user en el momento.
  2. 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ónUsa
patch() sobre una clase, función o atributo de módulopatch(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 existenspec_set=True (variante más estricta de spec)
El mock debe actuar como instancia, no como la clasecreate_autospec(RealThing, instance=True)
Mocks planos heredados que aún no puedes reescribircomo 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

Recursos

¿Construyendo una funcionalidad con IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.

Habla con nosotros · Lee el blog