Introducción: ¿Por qué las pruebas sin mocks son como un café sin cafeína?
\n\nImagine: está escribiendo una aplicación en TypeScript que envía correos electrónicos, se comunica con una base de datos y accede a una API externa. Ejecuta las pruebas y, de repente, todo falla porque el servidor de correo no está disponible temporalmente. ¿Le suena conocido?
\n\nAquí es donde entran en escena los objetos mock (simulaciones) y las fixtures (datos de prueba). Permiten aislar el código del mundo exterior y hacer que las pruebas sean rápidas, confiables y predecibles. En este artículo en español, analizaremos con ejemplos reales cómo usar correctamente los mock en TypeScript, qué tipos de fixtures para TypeScript existen y cómo no cometer errores.
\n\nEl artículo está escrito para PythonLib, un portal educativo donde enseñamos a escribir código de calidad. ¡Allá vamos!
\n\n¿Qué son los objetos mock y las fixtures?
\n\nObjetos Mock: actores dobles en su código
\n\nUn objeto mock (o simplemente mock) es un reemplazo de un objeto real que imita su comportamiento. Usted dice: "Cuando se llame al método sendEmail(), devuelve true". Y la prueba no accede al correo real.
\n\nEn TypeScript, los mocks se crean más a menudo con las bibliotecas Jest o Vitest. Permiten verificar que un método fue llamado, con qué argumentos y cuántas veces.
\n\nFixtures: datos preparados para las pruebas
\n\nLas fixtures son conjuntos de datos preparados de antemano. Por ejemplo, un objeto de usuario, una lista de pedidos o la configuración de una aplicación. En lugar de crearlos manualmente cada vez, almacena las fixtures en archivos separados.
\n\nLa combinación de mock + fixture es el estándar de pruebas en TypeScript en 2024.
\n\nPráctica: escribiendo pruebas con mocks y fixtures
\n\nConfiguración del entorno
\n\nPara los ejemplos, usaremos Jest (aunque todo funciona igual en Vitest). Asegúrese de tener las dependencias instaladas:
\n\nnpm install --save-dev jest @types/jest ts-jest\n\nAñada en jest.config.js:
module.exports = {\n preset: 'ts-jest',\n testEnvironment: 'node',\n};\n\nEjemplo 1: Mock del servicio de envío de correo
\n\nSupongamos que tenemos un servicio de notificaciones:
\n\n// notificationService.ts\nexport class EmailService {\n async sendEmail(to: string, subject: string, body: string): Promise<boolean> {\n // Envío real — no lo tocaremos\n return true;\n }\n}\n\nexport class NotificationManager {\n constructor(private emailService: EmailService) {}\n\n async notifyUser(email: string, message: string): Promise<string> {\n const result = await this.emailService.sendEmail(email, 'Notificación', message);\n return result ? 'Enviado' : 'Error';\n }\n}\n\nAhora escribamos una prueba con un objeto mock:
\n\n// notificationService.test.ts\nimport { NotificationManager, EmailService } from './notificationService';\n\ndescribe('NotificationManager', () => {\n it('debe enviar una notificación y devolver "Enviado"', async () => {\n // Creamos el mock\n const mockEmailService: jest.Mocked<EmailService> = {\n sendEmail: jest.fn().mockResolvedValue(true),\n };\n\n const manager = new NotificationManager(mockEmailService);\n const result = await manager.notifyUser('test@test.com', '¡Hola!');\n\n expect(result).toBe('Enviado');\n expect(mockEmailService.sendEmail).toHaveBeenCalledWith(\n 'test@test.com',\n 'Notificación',\n '¡Hola!'\n );\n });\n\n it('debe devolver "Error" si el envío falla', async () => {\n const mockEmailService: jest.Mocked<EmailService> = {\n sendEmail: jest.fn().mockResolvedValue(false),\n };\n\n const manager = new NotificationManager(mockEmailService);