How to Write Unit Tests in NestJS with Jest and Supertest

Updated: December 31, 2023 By: Guest Contributor Post a comment

Introduction

Testing is a critical step in the software development process. In this tutorial, we leverage Jest and Supertest to create reliable unit tests for applications built with NestJS, a Node.js framework. Learn how to ensure your NestJS services and controllers behave as expected under various conditions.

Setting Up the Environment

To start writing unit tests in NestJS, you must first set up your environment:

// Install Jest and the testing tools
$ npm install --save-dev @nestjs/testing jest @types/jest ts-jest
$ npm install --save-dev supertest @types/supertest

// Add a jest.config.js file
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};

// Add the following scripts to your package.json
"scripts": {
  "test": "jest",
  "test:watch": "jest --watch",
  "test:cov": "jest --coverage",
}

With Jest and Supertest installed, and the configuration in place, you’re ready to begin unit testing your NestJS application.

Testing NestJS Services

Begin with testing your services:

import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';

describe('MyService', () => {
  let service: MyService;

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

    service = module.get(MyService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  // Add more tests here
});

This is a simple test to check that the service is properly instantiated.

Testing Controllers with Supertest

Moving forward, combine efforts with Supertest to test controllers:

import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('AppController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });

  afterEach(async () => {
    await app.close();
  });
});

Here we’re using Supertest to perform end-to-end tests on the application’s endpoint.

Mocking Dependencies

Mocking is essential for isolating tests. NestJS provides tools for easy mocking:

import { MyService } from './my.service';

describe('MyService', () => {
  let service: MyService;
  let mockRepository: MockRepository;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        MyService,
        {
          provide: MyRepository,
          useClass: MockRepository
        }
      ],
    }).compile();

    service = module.get(MyService);
    mockRepository = module.get(MyRepository);
  });

  // Tests using the mocked repository here
});

Create realistic and controlled conditions by using mocked versions of your dependencies.

You can further enhance your unit tests by simulating more complex scenarios. NestJS’s powerful testing utilities enable developers to craft thorough test suites that cover all logic branches.

Creating Test Suites

While individual tests are useful, grouping similar tests into suites provides better organization and readability:

// Individual test suites with their own beforeEach, afterEach, and collection of `it` blocks for a comprehensive testing strategy

Use Jest’s powerful test suite capabilities to ensure every aspect of your NestJS application is verified.

Handling Asynchronous Code

In a Node.js environment, dealing with asynchronous operations is the norm. Here’s how you can test asynchronous service methods in NestJS:

it('should do async operation', async () => {
  await expect(service.someAsyncMethod()).resolves.toEqual(expectedOutcome);
});

Ensure your async functions behave as intended by properly resolving promises and managing exceptions.

Applying TDD

Test-Driven Development (TDD) is an approach where you write tests first, before coding the actual feature. Applying TDD with NestJS and Jest can significantly improve the quality and maintainability of your code.

Conclusion

Unit testing is a vital part of NestJS application development, ensuring your codebase is robust and functionally correct. With Jest and Supertest, NestJS apps can benefit from efficient, expressive, and powerful testing suites. By following this tutorial, you’re well on your way to mastering unit tests in your NestJS applications.