Unit Testing Made Easy with NestJS

What is NestJS?

NestJS is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript), and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

NestJS is heavily inspired by Angular and builds upon the same principles, including the use of modular architecture and the Dependency Injection design pattern. It also integrates with popular libraries such as Express and Socket.io, making it easy to create high-performance and scalable REST APIs and real-time applications.

Overall, NestJS is a great platform for building server-side applications and APIs and is particularly well-suited for building microservices.

What are unit tests?

Unit tests are individual tests that are designed to test the functionality of specific units of code in an application. A unit is the smallest testable part of an application and can be a function, method, or class. The purpose of unit testing is to validate that each unit of the application is working as intended and to catch bugs early on in the development process.

Unit tests are typically written by developers as they write the code for an application, and are run automatically every time the code is changed to ensure that the changes have not introduced any new bugs. By continuously running and passing a suite of unit tests, developers can have confidence that their code is working as intended and can safely make changes without breaking the application.

How to Unit Test in NestJS?

NestJS provides Jest as the default testing framework, which serves as a test runner and also provides assert functions and test-double utilities for mocking and spying.

To test a NestJS application, you can manually instantiate the classes being tested, a process often called isolated testing. This involves importing the classes and creating new instances of them in the test file. For example:

import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let catsController: CatsController;
  let catsService: CatsService;

  beforeEach(() => {
    catsService = new CatsService();
    catsController = new CatsController(catsService);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['test'];
      jest.spyOn(catsService, 'findAll').mockImplementation(() => result);

      expect(await catsController.findAll()).toBe(result);
    });
  });
});

Alternatively, you can use the @nestjs/testing package's Test class to provide an application execution context that mocks the full Nest runtime but allows you to easily manage class instances and override dependencies. The Test class has a createTestingModule() method that takes a module metadata object as its argument (the same object you pass to the @Module() decorator) and returns a TestingModule instance. This instance can be compiled and used to retrieve static instances of controllers and providers or dynamically resolve scoped providers (transient or request-scoped) using the resolve() method.

import { Test, TestingModule } from '@nestjs/testing'

Here's an example of using the Test class to test the CatsController and CatsService:

import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let catsController: CatsController;
  let catsService: CatsService;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
        controllers: [CatsController],
        providers: [CatsService],
      }).compile();

    catsService = moduleRef.get<CatsService>(CatsService);
    catsController = moduleRef.get<CatsController>(CatsController);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['test'];
      jest.spyOn(catsService, 'findAll').mockImplementation(() => result);

      expect(await catsController.findAll()).toBe(result);
    });
  });
});

You can use the Test class to test controllers, providers, pipes, guards, and interceptors. To test a controller, you can use the TestRequest() method to create a mock request object and the TestResponse() method to create a mock response object and use them to test the controller's methods.

Conclusion

Overall, unit testing is an important part of the software development process and NestJS makes it easy to set up and perform unit tests on your application's individual units. Using either the isolated testing method or the package test class, you can test controllers, providers, pipes, guards, and interceptors. You can use the mockImplementation() method from Jest to mock dependencies, and the TestRequest() and TestResponse() methods to create mock request and response objects for testing controllers. It is important to thoroughly test your units to ensure that your application is working as intended and to catch any bugs early on in the development process.

💡
Want to stay up-to-date on the latest trends and insights in our industry? Subscribe to the newsletter for regular updates and never miss out on valuable information!