Sling Academy
Home/Node.js/How to mock a service in NestJS unit tests

How to mock a service in NestJS unit tests

Last updated: January 01, 2024

Testing services in NestJS applications can be streamlined through effective mocking strategies, allowing for isolated and reliable unit tests.

Introduction

Uncovering how to mock services within NestJS unit tests is a crucial step to ensure that your modules are being tested in isolation, focusing solely on the behavior of the unit in question. This tutorial walks you through the basic to advanced procedures of mocking services, using Jest as the primary testing framework complemented by Jest’s powerful mocking functionalities, tailored for NestJS applications.

Setting Up the Testing Environment

Before delving into the mocking techniques, it is vital to establish a testing foundation. Ensure your NestJS project has Jest installed and properly configured. Verify that the jest.config.js and tsconfig.spec.json files are set up in accordance with the project requirements.

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
// tsconfig.spec.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2017",
    "noImplicitAny": false,
    "outDir": "./dist",
    "baseUrl": ".",
    "incremental": true
  }
}

Basic Mocking with Jest

Start by writing a simple NestJS service test. Create a mock of the service that will replace the real implementation in the tests. Jest’s jest.fn() and jest.mock() functions are commonly used to create these mock implementations.

// Example of mocking a basic service
const myServiceMock = {
  myMethod: jest.fn().mockReturnValue('mock value'),
};

test('should call myMethod', () => {
  const result = myServiceMock.myMethod('input');
  expect(result).toBe('mock value');
  expect(myServiceMock.myMethod).toHaveBeenCalledWith('input');
});

Integrating Mocks into NestJS Modules

When you begin to test NestJS modules consuming these services, integrate the mocking approach within the module’s testing suite by providing the mock as a replacement for the actual service provider.

import { Test } from '@nestjs/testing';
import { MyModule } from './my.module';
import { MyService } from './my.service';

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

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [MyModule],
    })
      .overrideProvider(MyService)
      .useValue(myServiceMock)
      .compile();

    myService = moduleRef.get(MyService);
  });

  it('should use the mocked service', async () => {
    const response = await myService.myMethod('test');
    expect(response).toBe('mock value');
  });
});

Advanced Mock Implementations

For more complex cases, such as mocking asynchronous methods or methods that return Promises, your mocks will need to leverage Jest’s mockResolvedValue() or mockRejectedValue() for creating promise-based mocks.

// Async service method mock
const asyncServiceMock = {
  asyncMethod: jest.fn().mockResolvedValue('async mock value'),
};

In cases where you need to reset or change the behavior of a mock between tests, use Jest’s mockReset() or mockImplementation() functions. This ensures that no state or behavior leakage occurs between tests.

beforeEach(() => {
  myServiceMock.myMethod.mockReset();
});

it('should alter mock implementation', () => {
  myServiceMock.myMethod.mockImplementation(() => 'new mock value');
  expect(myServiceMock.myMethod('input')).toBe('new mock value');
});

Mocking Dependencies Deeply Nested Within Modules

Some NestJS services might have multiple dependencies or compose several layers of services. For deeper nested dependencies, use more intricate mocking strategies like manual mocking or automatic mocks with jest.mock('../path/to/service').

// Manual mock of a deeply nested service
jest.mock('../path/to/deepNestedService', () => {
  return {
    DeepNestedService: jest.fn().mockImplementation(() => {
      return {
        deepMethod: jest.fn().mockReturnValue('deep mock value'),
      };
    })
  };
});

Testing Interactions and Integrations with Mocks

Moving beyond simple return values, sometimes the focus is on how components interact with each other. You can use spies and manual implementations to assert function call orders, or to ensure certain conditions are met before proceeding in your test scenarios.

// Inspecting interaction orders with mocks
const interactionMock = {
  firstMethod: jest.fn(() => 'first'),
  secondMethod: jest.fn(() => 'second'),
};

it('should assert method call order', () => {
  interactionMock.firstMethod();
  interactionMock.secondMethod();
  expect(interactionMock.firstMethod).toHaveBeenCalledBefore(interactionMock.secondMethod);
});

Throughout this tutorial, we also discussed strategies for mocking exceptions, third-party modules, and external APIs, establishing an exhaustive approach towards mocking services in NestJS unit tests.

Conclusion

The knowledge you’ve gained from this guidance on mocking services within NestJS is foundational for writing unit tests that are robust and maintainable. Mastering these techniques enhances testing reliability, helping address various intricacies such as asynchronous behavior, dependency hierarchies, and interactions between service layers. These methodologies empower you to build a more fault-tolerant and resilient application architecture.

Next Article: NestJS: How to Use ConfigService with TypeOrmModule

Previous Article: NestJS: How to inject service to validator constraint class

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates