¿Cuándo la realidad interfiere? ¿Por qué necesitamos mocks y fixtures en Go?
\n\nImagine que está probando una función que envía una notificación por correo electrónico. Cada ejecución de la prueba es un correo real en la bandeja de entrada. Después de diez pruebas, su bandeja está llena y sus colegas maldicen el spam. O peor: la función llama a una API externa que es de pago o funciona una vez al día.
\n\nEn el mundo de Go (o Golang, como se le llama a menudo), las pruebas no son solo una casilla de verificación en una lista. Son la base de la confiabilidad. Pero para que las pruebas sean rápidas e independientes, necesitamos aislar el código del mundo exterior. Aquí es donde entran en escena los objetos mock (mocks) y los fixtures (fixtures).
\n\nEn este artículo, analizaremos cómo reemplazar dependencias (mockear) y preparar datos (fixtures) correctamente en Go. Aprenderá a escribir pruebas que no fallen debido a la red, la base de datos o la hora del día. ¡Vamos allá!
\n\n¿Qué son Mock y Fixture en el contexto de Go?
\n\nObjetos Mock: dobles para sistemas complejos
\n\nLos objetos Mock son "suplentes" de software que imitan el comportamiento de componentes reales. En lugar de llamar a una base de datos real o un servicio HTTP, sustituimos un objeto que devuelve respuestas predefinidas.
\n\nEn Go, los mocks son especialmente importantes porque el lenguaje fomenta el trabajo con interfaces. Si su código depende de una interfaz, puede reemplazar fácilmente la implementación por una de prueba.
\n\nFixtures: preparando el escenario
\n\nLos Fixtures son datos o estados que deben establecerse antes de que comience la prueba. Por ejemplo, crear una base de datos temporal, llenarla con registros de prueba o configurar el sistema de archivos.
\n\nA diferencia de los mocks, que reemplazan el comportamiento, los fixtures preparan el entorno. Responden a la pregunta: "¿Qué debe estar listo para que la prueba pueda comenzar?"
\n\nEscribiendo mocks en Go: de lo simple a lo complejo
\n\nLa forma más simple: implementación manual de la interfaz
\n\nGo no requiere bibliotecas especiales para crear mocks. Si tiene una interfaz, puede escribir su implementación de prueba manualmente. Esto es ideal para proyectos pequeños.
\n\nSupongamos que tenemos una interfaz para enviar notificaciones:
\n\npackage notifier\n\ntype Notifier interface {\n Send(to string, message string) error\n}\n\n// Implementación real (no para pruebas)\ntype EmailNotifier struct{}\n\nfunc (e *EmailNotifier) Send(to string, message string) error {\n // Envío de email real\n return nil\n}\n\nEn el archivo de prueba creamos un mock:
\n\npackage notifier\n\nimport "testing"\n\ntype MockNotifier struct {\n Called bool\n LastTo string\n LastMsg string\n}\n\nfunc (m *MockNotifier) Send(to string, msg string) error {\n m.Called = true\n m.LastTo = to\n m.LastMsg = msg\n return nil\n}\n\nfunc TestSendAlert(t *testing.T) {\n mock := &MockNotifier{}\n // Usamos el mock en la función que acepta Notifier\n SendAlert(mock, "user@test.com", "Hello!")\n \n if !mock.Called {\n t.Error("El método Send no fue llamado")\n }\n if mock.LastTo != "user@test.com" {\n t.Errorf("Destinatario incorrecto: %s", mock.LastTo)\n }\n}\n\nVentajas: control total, sin dependencias.
\nDesventajas: mucho código repetitivo para interfaces complejas.
Usando testify/mock: potente y conveniente
\n\nLa biblioteca testify es el estándar de facto para mocks en Go. Proporciona un marco flexible para crear mocks con verificación de argumentos y número de llamadas.
Instalación:
\ngo get github.com/stretchr/testify\n\nEjemplo usando testify/mock:
\n\npackage user\n\nimport (\n "testing"\n "github.com/stretchr/testify/mock"\n)\n\n// Definimos la interfaz\ntype UserRepository interface {\n GetUser(id int) (string, error)\n}\n\n// Creamos la estructura mock\ntype