Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/test-mock-external-services.md
1---2title: Mock External Services in Tests3impact: HIGH4impactDescription: Ensures fast, reliable, deterministic tests5tags: testing, mocking, external-services, jest6---78## Mock External Services in Tests910Never 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.1112**Incorrect (calling real APIs and databases):**1314```typescript15// Call real APIs in tests16describe('PaymentService', () => {17it('should process payment', async () => {18const service = new PaymentService(new StripeClient(realApiKey));19// Hits real Stripe API!20const result = await service.charge('tok_visa', 1000);21// Slow, costs money, flaky22});23});2425// Use real database26describe('UsersService', () => {27beforeEach(async () => {28await connection.query('DELETE FROM users'); // Modifies real DB29});3031it('should create user', async () => {32const user = await service.create({ email: '[email protected]' });33// Side effects on shared database34});35});3637// Incomplete mocks38const mockHttpService = {39get: jest.fn().mockResolvedValue({ data: {} }),40// Missing error scenarios, missing other methods41};42```4344**Correct (mock all external dependencies):**4546```typescript47// Mock HTTP service properly48describe('WeatherService', () => {49let service: WeatherService;50let httpService: jest.Mocked<HttpService>;5152beforeEach(async () => {53const module = await Test.createTestingModule({54providers: [55WeatherService,56{57provide: HttpService,58useValue: {59get: jest.fn(),60post: jest.fn(),61},62},63],64}).compile();6566service = module.get(WeatherService);67httpService = module.get(HttpService);68});6970it('should return weather data', async () => {71const mockResponse = {72data: { temperature: 72, humidity: 45 },73status: 200,74statusText: 'OK',75headers: {},76config: {},77};7879httpService.get.mockReturnValue(of(mockResponse));8081const result = await service.getWeather('NYC');8283expect(result).toEqual({ temperature: 72, humidity: 45 });84});8586it('should handle API timeout', async () => {87httpService.get.mockReturnValue(88throwError(() => new Error('ETIMEDOUT')),89);9091await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable');92});9394it('should handle rate limiting', async () => {95httpService.get.mockReturnValue(96throwError(() => ({97response: { status: 429, data: { message: 'Rate limited' } },98})),99);100101await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException);102});103});104105// Mock repository instead of database106describe('UsersService', () => {107let service: UsersService;108let repo: jest.Mocked<Repository<User>>;109110beforeEach(async () => {111const mockRepo = {112find: jest.fn(),113findOne: jest.fn(),114save: jest.fn(),115delete: jest.fn(),116createQueryBuilder: jest.fn(),117};118119const module = await Test.createTestingModule({120providers: [121UsersService,122{ provide: getRepositoryToken(User), useValue: mockRepo },123],124}).compile();125126service = module.get(UsersService);127repo = module.get(getRepositoryToken(User));128});129130it('should find user by id', async () => {131const mockUser = { id: '1', name: 'John', email: '[email protected]' };132repo.findOne.mockResolvedValue(mockUser);133134const result = await service.findById('1');135136expect(result).toEqual(mockUser);137expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } });138});139});140141// Create mock factory for complex SDKs142function createMockStripe(): jest.Mocked<Stripe> {143return {144paymentIntents: {145create: jest.fn(),146retrieve: jest.fn(),147confirm: jest.fn(),148cancel: jest.fn(),149},150customers: {151create: jest.fn(),152retrieve: jest.fn(),153},154} as any;155}156157// Mock time for time-dependent tests158describe('TokenService', () => {159beforeEach(() => {160jest.useFakeTimers();161jest.setSystemTime(new Date('2024-01-15'));162});163164afterEach(() => {165jest.useRealTimers();166});167168it('should expire token after 1 hour', async () => {169const token = await service.createToken();170171// Fast-forward time172jest.advanceTimersByTime(61 * 60 * 1000);173174expect(await service.isValid(token)).toBe(false);175});176});177```178179Reference: [Jest Mocking](https://jestjs.io/docs/mock-functions)180