Introduction: Why tests without mocks are like coffee without caffeine?
\n\nImagine: you're writing a TypeScript application that sends email, communicates with a database, and calls an external API. You run your tests — and suddenly everything fails because the mail server is temporarily unavailable. Sound familiar?
\n\nThis is where mock objects (stubs) and fixtures (test data) come into play. They allow you to isolate your code from the outside world and make tests fast, reliable, and predictable. In this article, we'll use real examples to break down how to properly use mocks in TypeScript, what kinds of fixtures for TypeScript exist, and how to avoid common pitfalls.
\n\nThis article is written for PythonLib — an educational portal where we teach how to write quality code. Let's go!
\n\nWhat are mock objects and fixtures?
\n\nMock objects: stunt doubles in your code
\n\nA mock object (or simply mock) is a replacement for a real object that mimics its behavior. You say: "When the sendEmail() method is called, return true." And the test doesn't touch the real email server.
\n\nIn TypeScript, mocks are most often created using Jest or Vitest libraries. They allow you to verify that a method was called, with what arguments, and how many times.
\n\nFixtures: prepared data for tests
\n\nFixtures are pre-prepared sets of data. For example, a user object, an order list, or an application configuration. Instead of creating them manually every time, you store fixtures in separate files.
\n\nThe combination of mock + fixture is the standard for TypeScript testing in 2024.
\n\nPractice: writing tests with mocks and fixtures
\n\nEnvironment setup
\n\nFor the examples, we'll use Jest (though everything works the same in Vitest). Make sure you have the dependencies installed:
\n\nnpm install --save-dev jest @types/jest ts-jest\n\nAdd to jest.config.js:
module.exports = {\n preset: 'ts-jest',\n testEnvironment: 'node',\n};\n\nExample 1: Mocking an email sending service
\n\nLet's say we have a notification service:
\n\n// notificationService.ts\nexport class EmailService {\n async sendEmail(to: string, subject: string, body: string): Promise<boolean> {\n // Real sending — we won't touch it\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, 'Notification', message);\n return result ? 'Sent' : 'Error';\n }\n}\n\nNow let's write a test with a mock object:
\n\n// notificationService.test.ts\nimport { NotificationManager, EmailService } from './notificationService';\n\ndescribe('NotificationManager', () => {\n it('should send a notification and return "Sent"', async () => {\n // Create a 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', 'Hello!');\n\n expect(result).toBe('Sent');\n expect(mockEmailService.sendEmail).toHaveBeenCalledWith(\n 'test@test.com',\n 'Notification',\n 'Hello!'\n );\n });\n\n it('should return "Error" if sending fails', async () => {\n const mockEmailService: jest.Mocked<EmailService> = {\n sendEmail: jest.fn().mockResolvedValue(false),\n };\n\n const manager = new NotificationManager(mockEmailService);