Testing with NestJS and Jest
NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. It uses modern JavaScript, built with and fully supporting TypeScript. Jest is a delightful JavaScript Testing Framework with a focus on simplicity. It works with projects using Babel, TypeScript, Node.js, React, Angular, Vue.js, and more.
This documentation will guide you through the process of setting up and writing tests for your NestJS application using Jest.
Setting Up Testing Environment
NestJS comes with Jest pre-configured, but you need to install the necessary packages:
Copy pnpm add--save-dev jest @types/jest ts-jest @nestjs/testing
Create a jest.config.js
file in the root of your project with the following content:
Copy module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: 'src',
testRegex: '.*\\.spec\\.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
collectCoverageFrom: [
'**/*.(t|j)s'
],
coverageDirectory: '../coverage',
testEnvironment: 'node',
};
Writing Tests
Unit Testing
Unit tests focus on testing individual components or services. They ensure that each part of your application behaves as expected in isolation.
Example Service Test
Consider a simple CatsService
:
Copy // cats.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly cats = [];
findAll(): any[] {
return this.cats;
}
create(cat: any) {
this.cats.push(cat);
}
}
Create a test file named cats.service.spec.ts
:
Copy // 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>(CatsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should return an array', () => {
expect(service.findAll()).toBeInstanceOf(Array);
});
it('should create a cat', () => {
const cat = { name: 'Tom' };
service.create(cat);
expect(service.findAll()).toContain(cat);
});
});
Integration Testing
Integration tests ensure that different parts of the application work together as expected.
Example Controller Test
Consider a CatsController
that uses the CatsService
:
Copy // cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Get()
findAll() {
return this.catsService.findAll();
}
@Post()
create(@Body() cat: any) {
this.catsService.create(cat);
}
}
Create a test file named cats.controller.spec.ts
:
Copy // 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: [CatsService],
}).compile();
controller = module.get<CatsController>(CatsController);
service = module.get<CatsService>(CatsService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should return an array', () => {
const result = [];
jest.spyOn(service, 'findAll').mockImplementation(() => result);
expect(controller.findAll()).toBe(result);
});
it('should create a cat', () => {
const cat = { name: 'Tom' };
const createSpy = jest.spyOn(service, 'create');
controller.create(cat);
expect(createSpy).toHaveBeenCalledWith(cat);
});
});
E2E Testing
End-to-end (E2E) tests check the entire flow of your application, ensuring that all parts work together correctly.
Example E2E Test
NestJS provides a separate project structure for E2E tests. Create a test file named app.e2e-spec.ts
in the test
directory:
Copy // test/app.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('CatsController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/cats (GET)', () => {
return request(app.getHttpServer())
.get('/cats')
.expect(200)
.expect([]);
});
it('/cats (POST)', () => {
const cat = { name: 'Tom' };
return request(app.getHttpServer())
.post('/cats')
.send(cat)
.expect(201)
.expect((res) => {
expect(res.body.name).toEqual(cat.name);
});
});
afterAll(async () => {
await app.close();
});
});
Running Tests
To run your tests, use the following command:
For E2E tests, use:
Conclusion
By setting up Jest with NestJS, you can write comprehensive tests that ensure the correctness of your application at different levels. Unit tests validate individual components, integration tests check the interaction between components, and E2E tests verify the application's behavior as a whole. Following these practices will help you maintain a robust and reliable codebase.
Last updated 11 months ago