Retseptlar6 min read

Suites

Suites - bu TypeScript dependency injection freymvorklari uchun open-source unit-testing freymvorki. U mock'larni qo'lda yaratish, ko'p konfiguratsiyali uzun test setup yozish yoki

Suites - bu TypeScript dependency injection freymvorklari uchun open-source unit-testing freymvorki. U mock'larni qo'lda yaratish, ko'p konfiguratsiyali uzun test setup yozish yoki type'siz test double'lar (mocks, stubs) bilan ishlashga muqobil sifatida ishlatiladi.

Suites runtime vaqtida NestJS servislaridan metadata'ni o'qiydi va barcha dependency'lar uchun to'liq typed mock'larni avtomatik yaratadi. Bu boilerplate mock setup'ni yo'qotadi va type-safe testlarni ta'minlaydi. Suites'ni Test.createTestingModule() bilan birga ishlatish mumkin bo'lsa-da, u ayniqsa focused unit testing'da kuchli. Modul wiring, dekoratorlar, guard'lar va interceptor'larni tekshirishda Test.createTestingModule() dan foydalaning. Avtomatik mock yaratish bilan tez unit testlar uchun esa Suites'dan foydalaning.

Modulga asoslangan testing haqida ko'proq ma'lumot uchun testing fundamentals bobini ko'ring.

Note

Suites third-party paket bo'lib, NestJS core jamoasi tomonidan qo'llab-quvvatlanmaydi. Muammolarni mos repository ga yuboring.

Boshlash

Ushbu qo'llanma Suites yordamida NestJS servislarini test qilishni ko'rsatadi. U isolated testing (barcha dependency'lar mock qilingan) va sociable testing (tanlangan real implementatsiyalar ishlatiladigan) yondashuvlarini qamrab oladi.

Suites'ni o'rnatish

NestJS runtime dependency'lari o'rnatilganini tekshiring:

Terminal
1$ npm install @nestjs/common @nestjs/core reflect-metadata

Suites core, NestJS adapter va doubles adapter'ni o'rnating:

Terminal
1$ npm install --save-dev @suites/unit @suites/di.nestjs @suites/doubles.jest

Doubles adapter (@suites/doubles.jest) Jest'ning mocking imkoniyatlari ustiga wrapper beradi. U type-safe test double yaratadigan mock() va stub() funksiyalarini taqdim etadi.

Jest va TypeScript mavjudligiga ishonch hosil qiling:

Terminal
1$ npm install --save-dev ts-jest @types/jest jest typescript
Expand if you're using Vitest
Terminal
1$ npm install --save-dev @suites/unit @suites/di.nestjs @suites/doubles.vitest
Expand if you're using Sinon
Terminal
1$ npm install --save-dev @suites/unit @suites/di.nestjs @suites/doubles.sinon

Type definition'larni sozlash

Loyiha root'ida global.d.ts yarating:

TypeScript
1/// <reference types="@suites/doubles.jest/unit" />
2/// <reference types="@suites/di.nestjs/types" />

Namuna service yaratish

Bu qo'llanmada ikki dependency'ga ega sodda UserService ishlatiladi:

TypeScript
user.repository
1import { Injectable } from '@nestjs/common';
2
3@Injectable()
4export class UserRepository {
5  async findById(id: string): Promise<User | null> {
6    // Database query
7  }
8
9  async save(user: User): Promise<User> {
10    // Database save
11  }
12}
TypeScript
user.service
1import { Injectable, NotFoundException } from '@nestjs/common';
2import { Logger } from '@nestjs/common';
3
4@Injectable()
5export class UserService {
6  constructor(
7    private repository: UserRepository,
8    private logger: Logger,
9  ) {}
10
11  async findById(id: string): Promise<User> {
12    const user = await this.repository.findById(id);
13    if (!user) {
14      throw new NotFoundException(`User ${id} not found`);
15    }
16    this.logger.log(`Found user ${id}`);
17    return user;
18  }
19
20  async create(email: string, name: string): Promise<User> {
21    const user = { id: generateId(), email, name };
22    await this.repository.save(user);
23    this.logger.log(`Created user ${user.id}`);
24    return user;
25  }
26}

Unit test yozish

Barcha dependency'lar mock qilingan isolated testlar yaratish uchun TestBed.solitary() dan foydalaning:

TypeScript
user.service.spec
1import { TestBed, type Mocked } from '@suites/unit';
2import { UserService } from './user.service';
3import { UserRepository } from './user.repository';
4import { Logger } from '@nestjs/common';
5
6describe('User Service Unit Spec', () => {
7  let userService: UserService;
8  let repository: Mocked<UserRepository>;
9  let logger: Mocked<Logger>;
10
11  beforeAll(async () => {
12    const { unit, unitRef } = await TestBed.solitary(UserService).compile();
13
14    userService = unit;
15    repository = unitRef.get(UserRepository);
16    logger = unitRef.get(Logger);
17  });
18
19  it('should find user by id', async () => {
20    const user = { id: '1', email: 'test@example.com', name: 'Test' };
21    repository.findById.mockResolvedValue(user);
22
23    const result = await userService.findById('1');
24
25    expect(result).toEqual(user);
26    expect(logger.log).toHaveBeenCalled();
27  });
28});

TestBed.solitary() constructor'ni tahlil qilib, barcha dependency'lar uchun typed mock yaratadi. Mocked<T> turi mock konfiguratsiyasi uchun IntelliSense qo'llab-quvvatlashini beradi.

Compile'dan oldin mock konfiguratsiyasi

Mock xatti-harakatini compile'dan oldin .mock().impl() orqali sozlang:

TypeScript
user.service.spec
1import { TestBed } from '@suites/unit';
2import { UserService } from './user.service';
3import { UserRepository } from './user.repository';
4
5describe('User Service Unit Spec - pre-configured', () => {
6  let unit: UserService;
7  let repository: Mocked<UserRepository>;
8  
9  beforeAll(async () => {
10    const { unit: underTest, unitRef } = await TestBed.solitary(UserService)
11      .mock(UserRepository)
12      .impl(stubFn => ({
13        findById: stubFn().mockResolvedValue({ id: '1', email: 'test@example.com', name: 'Test' })
14      }))
15      .compile();
16    
17    repository = unitRef.get(UserRepository);
18    unit = underTest;
19  })
20  
21  it('should find user with pre-configured mock', async () => {
22    const result = await unit.findById('1');
23    
24    expect(repository.findById).toHaveBeenCalled();
25    expect(result.email).toBe('test@example.com');
26  });
27});

stubFn parametri o'rnatilgan doubles adapter'ga mos keladi (Jest uchun jest.fn(), Vitest uchun vi.fn(), Sinon uchun sinon.stub()).

Real dependency'lar bilan testing

Muayyan dependency'lar uchun real implementatsiyalardan foydalanish uchun .expose() bilan TestBed.sociable() dan foydalaning:

TypeScript
user.service.spec
1import { TestBed, Mocked } from '@suites/unit';
2import { UserService } from './user.service';
3import { UserRepository } from './user.repository';
4import { Logger } from '@nestjs/common';
5
6describe('UserService - with real logger', () => {
7  let userService: UserService;
8  let repository: Mocked<UserRepository>;
9
10  beforeAll(async () => {
11    const { unit, unitRef } = await TestBed.sociable(UserService)
12      .expose(Logger)
13      .compile();
14
15    userService = unit;
16    repository = unitRef.get(UserRepository);
17  });
18
19  it('should log when finding user', async () => {
20    const user = { id: '1', email: 'test@example.com' };
21    repository.findById.mockResolvedValue(user);
22
23    await userService.findById('1');
24
25    // Logger actually executes, no mock needed
26  });
27});

.expose(Logger) Logger ni real implementatsiyasi bilan yaratadi, qolgan dependency'lar esa mock bo'lib qoladi.

Token-based dependency'lar

Suites custom injection token'larni (string yoki symbol) qo'llab-quvvatlaydi:

TypeScript
config.service
1import { Injectable, Inject } from '@nestjs/common';
2
3export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';
4
5@Injectable()
6export class ConfigService {
7  constructor(
8    @Inject(CONFIG_OPTIONS) private options: { apiKey: string },
9  ) {}
10
11  getApiKey(): string {
12    return this.options.apiKey;
13  }
14}

Token-based dependency'larga unitRef.get() orqali murojaat qiling:

TypeScript
config.service.spec
1import { TestBed } from '@suites/unit';
2import { ConfigService, CONFIG_OPTIONS, ConfigOptions } from './config.service';
3
4describe('Config Service Unit Spec', () => {
5  let configService: ConfigService;
6  let options: ConfigOptions;
7
8  beforeAll(async () => {
9    const { unit, unitRef } = await TestBed.solitary(ConfigService).compile();
10    configService = unit;
11
12    options = unitRef.get<ConfigOptions>(CONFIG_OPTIONS);
13  });
14
15  it('should return api key', () => { ... });
16});

mock() va stub() dan to'g'ridan-to'g'ri foydalanish

TestBed siz ishlatmoqchi bo'lmasangiz va to'g'ridan-to'g'ri nazorat kerak bo'lsa, doubles adapter paketi mock() va stub() funksiyalarini beradi:

TypeScript
user.service.spec
1import { mock } from '@suites/unit';
2import { UserRepository } from './user.repository';
3
4describe('User Service Unit Spec', () => {
5  it('should work with direct mocks', async () => {
6    const repository = mock<UserRepository>();
7    const logger = mock<Logger>();
8
9    const service = new UserService(repository, logger);
10
11    // ...
12  });
13});

mock() typed mock object yaratadi, stub() esa asosiy mocking kutubxonasini (bu misolda Jest) o'rab, mockResolvedValue() kabi metodlarni taqdim etadi. Bu funksiyalar o'rnatilgan doubles adapter'dan (@suites/doubles.jest) keladi va u test freymvorkining native mocking imkoniyatlarini moslashtiradi.

Hint

mock() funksiyasi @golevelup/ts-jest dagi createMock ga muqobil hisoblanadi. Ikkalasi ham typed mock object yaratadi. createMock haqida ko'proq ma'lumot uchun testing fundamentals bobiga qarang.

Xulosa

Test.createTestingModule() dan quyidagilar uchun foydalaning:

  • Validating module configuration and provider wiring
  • Testing decorators, guards, interceptors, and pipes
  • Verifying dependency injection across modules
  • Testing full application context with middleware

Suites'dan quyidagilar uchun foydalaning:

  • Fast unit tests focused on business logic
  • Automatic mock generation for multiple dependencies
  • Type-safe test doubles with IntelliSense

Testlarni maqsadiga qarab ajrating: alohida service xatti-harakatini tekshiradigan unit testlar uchun Suites'dan, modul konfiguratsiyasini tekshiradigan integration testlar uchun esa Test.createTestingModule() dan foydalaning.

Qo'shimcha ma'lumot:

  • Suites Documentation
  • Suites GitHub Repository
  • NestJS Testing Documentation