How To Write Integration Tests in NestJS?
Last Updated :
23 Sep, 2024
Integration testing is an important part of software development, ensuring that various parts of your application work together as expected. In NestJS, integration testing allows you to test the interaction between different modules, services, and controllers.
In this article, we will guide you through the process of writing integration tests in NestJS, covering essential concepts, tools, and examples to help you implement robust tests for your application.
What are Integration Tests?
Integration testing is the phase in software testing where individual components or systems are combined and tested as a group. Unlike unit testing, which focuses on testing isolated components in isolation, integration testing ensures that these components work together as expected.
In NestJS, integration tests typically involve testing how modules, controllers, services, and external resources (like databases and APIs) interact. It ensures that your application performs correctly under real-world conditions.
Steps To Write Integration Tests in NestJS
Step 1: Set Up Your Testing Environment
npm i -g @nestjs/cli
nest new project-name
Step 2: Create the Patient and Appointment Modules
nest g module patient
nest g module appointment
Step 3: Create the Services and Controllers
nest g service patient
nest g controller patient
nest g service appointment
nest g controller appointment
Folder Structure
Folder StructureDependencies
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
Step 4: Write Integration Tests
Create a test file for your appointment module
appointment.model.ts
appointment.module.ts
appointment.service.spec.ts
appointment.service.ts
JavaScript
//src/appointment/appointment.model.ts
export interface Appointment {
patientId: number;
startTime: Date;
endTime: Date;
confirmed: boolean;
}
JavaScript
//src/appointment/appointment.module.ts
import { Module } from '@nestjs/common';
import { PatientModule } from '../patient/patient.module';
import { AppointmentService } from './appointment.service';
@Module({
imports: [PatientModule],
providers: [AppointmentService],
})
export class AppointmentModule { }
JavaScript
//src/appointment/appointment.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { PatientModule } from '../patient/patient.module';
import { PatientService } from '../patient/patient.service';
import { AppointmentService } from './appointment.service';
describe('AppointmentService', () => {
let service: AppointmentService;
let patientService: PatientService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [PatientModule],
providers: [AppointmentService],
}).compile();
service = module.get<AppointmentService>(AppointmentService);
patientService = module.get(PatientService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should schedule an unconfirmed appointment for a user on success', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = new Date('2022-01-01T15:00:00Z');
const { id: patientId } = await patientService.register({
name: 'John Doe',
});
const newAppointment = await service.scheduleAppointment({
patientId,
startTime,
endTime,
});
expect(newAppointment).toEqual({
patientId,
startTime,
endTime,
confirmed: false,
});
});
it('should throw an error when end time is before start time', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = new Date('2022-01-01T13:00:00Z');
await expect(
service.scheduleAppointment({
patientId: 1,
startTime,
endTime,
}),
).rejects.toThrowError("appointment's endTime should be after startTime");
});
it('should throw an error when end time is equal to start time', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = startTime;
await expect(
service.scheduleAppointment({
patientId: 1,
startTime,
endTime,
}),
).rejects.toThrowError("appointment's endTime should be after startTime");
});
it('should throw an error when end time is in the next day', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = new Date('2022-01-02T00:00:00Z');
await expect(
service.scheduleAppointment({
patientId: 1,
startTime,
endTime,
}),
).rejects.toThrowError(
"appointment's endTime should be in the same day as start time's",
);
});
it('should throw an error when end time is in same day and hour of next month', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = new Date('2022-02-01T14:00:00Z');
await expect(
service.scheduleAppointment({
patientId: 1,
startTime,
endTime,
}),
).rejects.toThrowError(
"appointment's endTime should be in the same day as start time's",
);
});
it('should throw an error when end time is in same day, hour and month of the next year', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = new Date('2023-01-01T14:00:00Z');
await expect(
service.scheduleAppointment({
patientId: 1,
startTime,
endTime,
}),
).rejects.toThrowError(
"appointment's endTime should be in the same day as start time's",
);
});
it('should throw an error when the patient does not exist', async () => {
const startTime = new Date('2022-01-01T14:00:00Z');
const endTime = new Date('2022-01-01T15:00:00Z');
await expect(
service.scheduleAppointment({
patientId: 1,
startTime,
endTime,
}),
).rejects.toThrowError('Patient does not exist');
});
});
JavaScript
//src/appointment/appointment.service.ts
import { Injectable } from '@nestjs/common';
import { PatientService } from '../patient/patient.service';
import { Appointment } from './appointment.model';
export interface AppointmentInput {
patientId: number;
startTime: Date;
endTime: Date;
}
@Injectable()
export class AppointmentService {
constructor(private readonly patientService: PatientService) { }
public async scheduleAppointment(
appointmentData: AppointmentInput,
): Promise<Appointment> {
if (appointmentData.endTime <= appointmentData.startTime) {
throw new Error("appointment's endTime should be after startTime");
}
if (this.endTimeIsInTheNextDay(appointmentData)) {
throw new Error(
"appointment's endTime should be in the same day as start time's",
);
}
const patientExists = await this.patientService.doesPatientExist(
appointmentData.patientId,
);
if (!patientExists) {
throw new Error('Patient does not exist');
}
return {
...appointmentData,
confirmed: false,
};
}
private endTimeIsInTheNextDay(appointmentData: AppointmentInput): boolean {
const differentDays =
appointmentData.endTime.getUTCDate() !==
appointmentData.startTime.getUTCDate();
const differentMonths =
appointmentData.endTime.getUTCMonth() !==
appointmentData.startTime.getUTCMonth();
const differentYears =
appointmentData.endTime.getUTCFullYear() !==
appointmentData.startTime.getUTCFullYear();
return differentDays || differentMonths || differentYears;
}
}
Create a test file for your patient module
patient.model.ts
patient.modules.ts
patient.service.spec.ts
patient.service.ts
JavaScript
//src/patient/patient.model.ts
export interface Patient {
id: number;
name: string;
}
JavaScript
//src/patient/patient.module.ts
import { Module } from '@nestjs/common';
import { PatientService } from './patient.service';
@Module({
providers: [PatientService],
exports: [PatientService],
})
export class PatientModule { }
JavaScript
//src/patient/patient.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { PatientService } from './patient.service';
describe('PatientService', () => {
let service: PatientService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PatientService],
}).compile();
service = module.get<PatientService>(PatientService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('register', () => {
it('should return a new patient with given name', async () => {
const newPatient = await service.register({ name: 'John Doe' });
expect(newPatient).toEqual({
id: expect.any(Number),
name: 'John Doe',
});
});
it('should return different patients when called twice', async () => {
const firstPatient = await service.register({ name: 'John Doe' });
const secondPatient = await service.register({ name: 'John Doe' });
expect(firstPatient).not.toEqual(secondPatient);
});
});
describe('doesPatientExist', () => {
it('should return false when no patient was registered', async () => {
const patientId = 1;
const exists = await service.doesPatientExist(patientId);
expect(exists).toBe(false);
});
it('should return true when patient was registered', async () => {
const { id: patientId } = await service.register({ name: 'John Doe' });
const exists = await service.doesPatientExist(patientId);
expect(exists).toBe(true);
});
});
});
JavaScript
//src/patient/patient.service.ts
import { Injectable } from '@nestjs/common';
import { Patient } from './patient.model';
export interface PatientInput {
name: string;
}
@Injectable()
export class PatientService {
private readonly patients: Patient[] = [];
private nextId = 1;
public async register(patientInput: PatientInput): Promise<Patient> {
const newPatient = {
id: this.nextId++,
name: patientInput.name,
};
this.patients.push(newPatient);
return newPatient;
}
public async doesPatientExist(patientId: number): Promise<boolean> {
return this.patients.some((patient) => patient.id === patientId);
}
}
Create a app files in src folder
app.controller.spec.ts
app.controller.ts
app.module.ts
app.service.ts
main.ts
JavaScript
//src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
JavaScript
//src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PatientModule } from './patient/patient.module';
import { AppointmentModule } from './appointment/appointment.module';
@Module({
imports: [PatientModule, AppointmentModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
JavaScript
//src/app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
JavaScript
//src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) { }
@Get()
getHello(): string {
return this.appService.getHello();
}
}
JavaScript
//src/app.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
Create a new Test folder with this file inside
app.e2e-spec.ts
JavaScript
//test/app.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { PatientModule } from '../src/patient/patient.module';
import { PatientService } from '../src/patient/patient.service';
describe('PatientModule (e2e)', () => {
let app: INestApplication;
let patientService = {
findAll: () => [{ id: 1, name: 'John Doe', age: 30 }],
create: (dto: any) => ({ id: 2, ...dto }),
};
// Setup before tests
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [PatientModule],
})
.overrideProvider(PatientService)
.useValue(patientService)
.compile();
app = moduleFixture.createNestApplication();
await app.init();
});
// Integration test for GET /patients endpoint
it('/patients (GET)', () => {
return request(app.getHttpServer())
.get('/patients')
.expect(200)
.expect([{ id: 1, name: 'John Doe', age: 30 }]);
});
// Integration test for POST /patients endpoint
it('/patients (POST)', () => {
return request(app.getHttpServer())
.post('/patients')
.send({ name: 'Jane Doe', age: 28 })
.expect(201)
.expect({ id: 2, name: 'Jane Doe', age: 28 });
});
// Cleanup after tests
afterAll(async () => {
await app.close();
});
});
Step 5: Start and Run the Test
npm start test
How To Write Integration Tests in NestJSRun the test using the following command
npm test --
Run the test
Similar Reads
Testing in Rails: How to Write Unit Tests, Integration Tests, and Use RSpec
Testing is a crucial part of Rails development, ensuring your application runs smoothly and without errors. In this article, weâll explore how to write unit tests, integration tests, and use RSpec in Rails. With clear examples and a demo application, youâll learn how to confidently test your Rails a
7 min read
How to Integrate Paytm Test API in Node.js ?
Paytm stands for Pay through mobile is used for online transactions. We can integrate it with our node.js application using Paytm developer API. This API can be used for testing as well as for development purposes. Â There are two methods for doing so: Test API and Production API. Production API will
2 min read
How To Perform Unit Test in NestJS?
NestJS is a Node.js framework for building efficient, reliable and scalable server-side applications. It is built using TypeScript and It offers support for unit testing through the Jest testing framework. In this article, we will learn about performing unit testing in a NestJs.Table of ContentWhat
5 min read
Introduction to GraphQL with NestJS
NestJS is a progressive NodeJS framework that uses TypeScript to build efficient and scalable server-side applications. Combining NestJS with GraphQL, a powerful query language for APIs, offers a robust solution for creating modern, maintainable, and highly performant web applications. In this artic
3 min read
How to create and write tests for API requests in Postman?
Postman is an API(utility programming interface) development device that enables to construct, take a look at and alter APIs. It could make numerous varieties of HTTP requests(GET, POST, PUT, PATCH), store environments for later use, and convert the API to code for various languages(like JavaScript,
3 min read
Introduction to Testing in MERN
Testing in MERN (MongoDB, ExpressJS, React, NodeJS) applications is important for ensuring the reliability, functionality, and stability of your software. Testing helps identify bugs early in the development process, maintain code quality, and build confidence in your application's behavior. In this
4 min read
Big Bang Integration Testing
Integration testing is a type of testing that is used to check the functionality of integrated components of a software system. It is usually performed after unit testing and before validation testing. In integration testing, individual software components are combined and tested as a group. The pur
8 min read
How to Setup Continuous Integration
Continuous Integration (CI) is a crucial practice in modern software development, fostering a streamlined and collaborative workflow. It involves the frequent integration of code changes into a shared repository, enabling rapid feedback, early bug detection, and efficient collaboration among develop
9 min read
How to link data files to requests in Postman for iterative testing?
Postman may be beneficial when trying out APIs that require file uploads or while sending complicated JSON payloads. In this article, you will explore the way to upload documents in Postman and alternative approaches to make it well. Let's dive in.In the area of API testing and development, managing
2 min read
Integration Testing Tool - Software Testing
Integration Testing Tools are essential in the software development lifecycle, designed to streamline the process of testing how different components of an application work together. Unlike unit testing, which focuses on individual components, integration testing ensures that multiple modules or ser
9 min read