Testing is essential in software development, ensuring applications are reliable and bug-free. In NestJS, Jest is a widely-used, powerful testing framework that provides comprehensive tools to write and execute test cases efficiently. This guide provides essential steps for setting up and configuring Jest in your NestJS application.

Why Jest?

By incorporating Jest, you can enhance the quality and stability of your software, boosting developer and user trust. With Jest's strong testing practices, you can foster a culture of excellence, improving productivity and overall success in your software development journey.

Testing with Jest

Step 1: Set Up the Environment

Before starting the testing process, it is necessary to prepare the NestJS application. If you don't have it, you can install the NestJS CLI globally and create a new project using it.

Install NestJS CLI

npm install -g @nestjs/cli

Create a new NestJS project

nest new my-nest-app

Once your project is set up, you can proceed to install Jest and its necessary dependencies, including @types/jest and ts-jest.

Install Jest and its dependencies

cd my-nest-app
npm install --save-dev jest @types/jest ts-jest

Create a jest.config.js file in the root directory to configure Jest for your application. This file allows you to customize the behaviour of Jest according to your project requirements. Once the configuration is complete, you can start writing and running test cases efficiently using Jest in your NestJS application.


Also Read: From Code to Cloud: Building and Publishing an NX NestJS Library with Prisma and PostgreSQL


Create a jest.config.js file

// jest.config.js
module.exports = {
roots: ['<rootDir>/src'],
testMatch: ['/tests//*.spec.ts'],
transform: {
'^.+\.tsx?$': 'ts-jest',
},
};

Content of jest.config.js

Step 2: Write Your First Test

Create tests for MathService's add and multiply methods by first defining a test module using the Test.createTestingModule method.

In this module, compile the application to get an instance of MathService. Then, using the expect function, write test cases for each method to ensure the service returns the expected results for the given inputs.

For example, with the add method, you can test the addition of two numbers and check that the result matches the expected amount. Similarly, experiment with multiplying two numbers using the multiply method and check that the result is consistent with the expected product.

// src/math.service.ts
@Injectable()
export class MathService {
add(a: number, b: number): number {
return a + b;
}

multiply(a: number, b: number): number {
return a * b;
}
}

Content of src/math.service.ts

// src/math.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { MathService } from './math.service';

describe('MathService', () => {
let mathService: MathService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MathService],
}).compile();

mathService = module.get<MathService>(MathService);
});

it('should add two numbers', () => {
const result = mathService.add(2, 3);
expect(result).toBe(5);
});
it('should multiply two numbers', () => {
const result = mathService.multiply(2, 3);
expect(result).toBe(6);
});
});

Content of src/math.service.spec.ts

Step 3: Test NestJS Controllers

To test NestJS controllers with Jest, start by creating an AppController that handles a GET request to the /hello endpoint.

You can use Test.createTestingModule to define a test module that contains an AppController and any necessary services, such as MathService.

Next, write tests for the controller methods, mocking possible dependencies if necessary. For example, simulate a request to the /hello endpoint and check if the controller returns the expected response.

Additionally, you can mock MathService to ensure that controller logic is handled correctly. These tests help ensure that the AppController behaves correctly in different scenarios.

// src/app.controller.ts
@Controller()
export class AppController {
constructor(private readonly mathService: MathService) {}

@Get('hello')
getHello(): string {
return 'Hello, NestJS!';
}
}

Content of src/app.controller.ts

// src/app.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { MathService } from './math.service';

describe('AppController', () => {
let appController: AppController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AppController],providers: [MathService],
}).compile();
appController = module.get<AppController>(AppController);
});
it('should return "Hello, NestJS!"', () => {
const result = appController.getHello();
expect(result).toBe('Hello, NestJS!');
});
});

Content of src/app.controller.spec.ts


Also Read: AsyncAPI Docs For Event-driven applications


Step 4: Mock Dependencies

For more complex scenarios where controllers depend on services with external dependencies, such as a database, Jest's mock functions come to the rescue.

You can use the jest.fn() function to create example functions that mimic the behaviour of real dependencies. This approach allows you to isolate the operation of the controller during testing and ensure that its logic is tested independently and reliably.

When testing a controller, you can mock service methods and their expected return values. For example, if a controller endpoint retrieves data from a database through a service, you can mock the service function using jest.fn() to return predefined data instead of querying the database. Thus, the test focuses exclusively on the operation of the controller and not on the state of the database or connections.

By using powerful mocking techniques, you achieve faster and more stable tests because they do not depend on external resources. It also makes test setup and maintenance easier because you control the behaviour of external dependencies during testing.

Mocking is a powerful tool in Jest that allows developers to create comprehensive and reliable tests for NestJS controllers in various complex scenarios.

// src/app.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { MathService } from './math.service';

describe('AppController', () => {
let appController: AppController;
let mathService: MathService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [{provide: MathService,
useValue: {
add: jest.fn((a, b) => a + b),
multiply: jest.fn((a, b) => a * b),
},
},
],
}).compile();

appController = module.get<AppController>(AppController);

mathService = module.get<MathService>(MathService);
});

it('should return "Hello, NestJS!"', () => {
const result = appController.getHello();
expect(result).toBe('Hello, NestJS!');
});

it('should call MathService.add', () => {
appController.getHello();
expect(mathService.add).toHaveBeenCalledWith(2, 3);
});
});

Content of src/app.controller.spec.ts

Step 5: Testing Middleware and Guards

NestJS middleware and firewalls are important in solving operational problems and authenticating applications. Creating separate test files to test them according to the same models as in services and controllers is necessary to ensure they work correctly.

If it's a middleware, use Jest to create a test module and mock any external dependencies or services that the middleware depends on. Write test cases that simulate incoming requests and confirm that the middleware works as intended by modifying the requests or responses accordingly.

Similarly, create a test module for the guards and mock any dependencies such as authentication services or database queries. Write tests to cover different scenarios, such as authenticated and unauthenticated requests, and ensure that guards allow or deny access appropriately.

By fully testing the middleware and protections, developers can trust the reliability of these critical components and ensure that they effectively handle cross-domain issues and authentication in a NestJS application. Effective testing allows problems to be detected early, and adjustments can be made, resulting in safer and more reliable applications.

Conclusion

Writing comprehensive test cases in Jest for your NestJS application is crucial for ensuring code reliability and maintainability. Following the steps outlined in this guide, you can establish a robust testing framework covering all aspects of your application, including services, controllers, middleware, and guards.

Thorough testing allows for early bug detection and resolution, resulting in a more stable and efficient application. It also helps meet user requirements and delivers a positive user experience. By prioritizing testing and leveraging the power of Jest, you can build trustworthy and high-quality software that can handle real-world challenges.

Thank you for reading. Please consider subscribing if you liked the blog.