NestJS — це потужний і надійний фреймворк для розробки ефективних, надійних та масштабованих серверних застосунків. Тестування є важливою частиною розробки програмного забезпечення; воно забезпечує якість коду, підвищує його підтримуваність і спрощує налагодження. NestJS надає відмінну підтримку для тестування, і в цій статті ми розглянемо, як писати юніт-тести та інтеграційні тести в додатку NestJS, використовуючи TypeScript.
Налаштування проєкту NestJS
Спочатку налаштуємо проєкт NestJS, якщо ви ще цього не зробили.
npm i -g @nestjs/cli
nest new project-name
Перейдіть до вашої директорії проєкту:
cd project-name
Тестувальні фреймворки
NestJS використовує Jest як фреймворк для тестування за замовчуванням, який є потужною та гнучкою бібліотекою для тестування з вбудованою підтримкою TypeScript.
Jest дозволяє легко налаштовувати тестування та надає багатий набір функцій, включаючи мокінг, знімки (snapshots) та звіти про покриття.
Переконаймося, що необхідні пакети встановлені:
npm install --save-dev jest @types/jest ts-jest @nestjs/testing
Написання юніт-тестів
Юніт-тести — це маленькі, ізольовані тести, які перевіряють поведінку окремих компонентів, таких як сервіси або контролери, без зовнішніх залежностей, як-от бази даних або інші сервіси.
Приклад: Тестування Сервісу
Розглянемо простий CatsService
, який управляє додаванням та отриманням котів.
// src/cats/cats.service.ts
import { Injectable } from '@nestjs/common';
interface Cat {
id: number;
name: string;
age: number;
}
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
Тепер напишемо юніт-тести для CatsService
.
// src/cats/cats.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatsService } from './cats.service';
describe('CatsService', () => {
let service: CatsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CatsService],
}).compile();
service = module.get(CatsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should create a cat', () => {
const cat = { id: 1, name: 'Tom', age: 3 };
service.create(cat);
expect(service.findAll()).toEqual([cat]);
});
it('should return all cats', () => {
const cat1 = { id: 1, name: 'Tom', age: 3 };
const cat2 = { id: 2, name: 'Jerry', age: 2 };
service.create(cat1);
service.create(cat2);
expect(service.findAll()).toEqual([cat1, cat2]);
});
});
Тестування Контролера
Давайте додамо простий CatsController
.
// src/cats/cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatsService } from './cats.service';
interface Cat {
id: number;
name: string;
age: number;
}
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post()
create(@Body() cat: Cat) {
this.catsService.create(cat);
}
@Get()
findAll(): Cat[] {
return this.catsService.findAll();
}
}
Тепер напишемо юніт-тест для CatsController
.
// src/cats/cats.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
describe('CatsController', () => {
let controller: CatsController;
let service: CatsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CatsController],
providers: [
{
provide: CatsService,
useValue: {
create: jest.fn(),
findAll: jest.fn().mockReturnValue([{ id: 1, name: 'Tom', age: 3 }]),
},
},
],
}).compile();
controller = module.get(CatsController);
service = module.get(CatsService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should create a cat', () => {
const cat = { id: 1, name: 'Tom', age: 3 };
controller.create(cat);
expect(service.create).toHaveBeenCalledWith(cat);
});
it('should return all cats', () => {
expect(controller.findAll()).toEqual([{ id: 1, name: 'Tom', age: 3 }]);
});
});
Написання інтеграційних тестів
Інтеграційні тести перевіряють взаємодію між кількома компонентами, такими як контролери, сервіси та бази даних, щоб переконатися, що вони працюють разом як очікується.
Приклад: Тестування з базою даних
Для демонстрації інтеграційного тестування припустимо, що ми маємо сутність Cat
з використанням TypeORM.
// src/cats/cat.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Cat {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
age: number;
}
Нам знадобиться репозиторій для підключення нашого сервісу до бази даних.
// src/cats/cats.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cat } from './cat.entity';
@Injectable()
export class CatsService {
constructor(
@InjectRepository(Cat)
private readonly catsRepository: Repository<Cat>,
) {}
create(cat: Partial<Cat>): Promise<Cat> {
const newCat = this.catsRepository.create(cat);
return this.catsRepository.save(newCat);
}
findAll(): Promise<Cat[]> {
return this.catsRepository.find();
}
}
Далі напишемо інтеграційні тести.
Ми повинні налаштувати базу даних в пам'яті для тестування, наприклад, SQLite.
// src/cats/cats.service.integration.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CatsService } from './cats.service';
import { Cat } from './cat.entity';
describe('CatsService (Integration)', () => {
let service: CatsService;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
entities: [Cat],
synchronize: true,
}),
TypeOrmModule.forFeature([Cat]),
],
providers: [CatsService],
}).compile();
service = module.get(CatsService);
});
afterEach(async () => {
await service.create({ name: 'Tom', age: 3 });
await service.create({ name: 'Jerry', age: 2 });
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should create a cat', async () => {
const cat = { name: 'Tom', age: 3 };
const createdCat = await service.create(cat);
expect(createdCat).toEqual(expect.objectContaining(cat));
});
it('should return all cats', async () => {
const cats = await service.findAll();
expect(cats.length).toBe(2);
expect(cats).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: 'Tom', age: 3 }),
expect.objectContaining({ name: 'Jerry', age: 2 }),
]),
);
});
});
Висновок
Тестування додатків на NestJS є важливою частиною для забезпечення якості та підтримуваності. Написання юніт-тестів для ізольованих компонентів та інтеграційних тестів для перевірки взаємодії між компонентами дозволяє створювати надійні та стабільні додатки. Завдяки Jest і вбудованим тестовим утилітам NestJS, написання та виконання тестів стає простим та ефективним.
Тепер ви готові ефективно почати тестувати свої додатки на NestJS. Успіхів у програмуванні!
Перекладено з: Testing NestJS Applications: Unit and Integration Testing