--- title: Mock External Services in Tests impact: HIGH impactDescription: Ensures fast, reliable, deterministic tests tags: testing, mocking, external-services, jest --- ## Mock External Services in Tests Never call real external services (APIs, databases, message queues) in unit tests. Mock them to ensure tests are fast, deterministic, and don't incur costs. Use realistic mock data and test edge cases like timeouts and errors. **Incorrect (calling real APIs and databases):** ```typescript // Call real APIs in tests describe('PaymentService', () => { it('should process payment', async () => { const service = new PaymentService(new StripeClient(realApiKey)); // Hits real Stripe API! const result = await service.charge('tok_visa', 1000); // Slow, costs money, flaky }); }); // Use real database describe('UsersService', () => { beforeEach(async () => { await connection.query('DELETE FROM users'); // Modifies real DB }); it('should create user', async () => { const user = await service.create({ email: 'test@test.com' }); // Side effects on shared database }); }); // Incomplete mocks const mockHttpService = { get: jest.fn().mockResolvedValue({ data: {} }), // Missing error scenarios, missing other methods }; ``` **Correct (mock all external dependencies):** ```typescript // Mock HTTP service properly describe('WeatherService', () => { let service: WeatherService; let httpService: jest.Mocked; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ WeatherService, { provide: HttpService, useValue: { get: jest.fn(), post: jest.fn(), }, }, ], }).compile(); service = module.get(WeatherService); httpService = module.get(HttpService); }); it('should return weather data', async () => { const mockResponse = { data: { temperature: 72, humidity: 45 }, status: 200, statusText: 'OK', headers: {}, config: {}, }; httpService.get.mockReturnValue(of(mockResponse)); const result = await service.getWeather('NYC'); expect(result).toEqual({ temperature: 72, humidity: 45 }); }); it('should handle API timeout', async () => { httpService.get.mockReturnValue( throwError(() => new Error('ETIMEDOUT')), ); await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable'); }); it('should handle rate limiting', async () => { httpService.get.mockReturnValue( throwError(() => ({ response: { status: 429, data: { message: 'Rate limited' } }, })), ); await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException); }); }); // Mock repository instead of database describe('UsersService', () => { let service: UsersService; let repo: jest.Mocked>; beforeEach(async () => { const mockRepo = { find: jest.fn(), findOne: jest.fn(), save: jest.fn(), delete: jest.fn(), createQueryBuilder: jest.fn(), }; const module = await Test.createTestingModule({ providers: [ UsersService, { provide: getRepositoryToken(User), useValue: mockRepo }, ], }).compile(); service = module.get(UsersService); repo = module.get(getRepositoryToken(User)); }); it('should find user by id', async () => { const mockUser = { id: '1', name: 'John', email: 'john@test.com' }; repo.findOne.mockResolvedValue(mockUser); const result = await service.findById('1'); expect(result).toEqual(mockUser); expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } }); }); }); // Create mock factory for complex SDKs function createMockStripe(): jest.Mocked { return { paymentIntents: { create: jest.fn(), retrieve: jest.fn(), confirm: jest.fn(), cancel: jest.fn(), }, customers: { create: jest.fn(), retrieve: jest.fn(), }, } as any; } // Mock time for time-dependent tests describe('TokenService', () => { beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(new Date('2024-01-15')); }); afterEach(() => { jest.useRealTimers(); }); it('should expire token after 1 hour', async () => { const token = await service.createToken(); // Fast-forward time jest.advanceTimersByTime(61 * 60 * 1000); expect(await service.isValid(token)).toBe(false); }); }); ``` Reference: [Jest Mocking](https://jestjs.io/docs/mock-functions)