How to Test NestJS Applications with the Nest Testing Package

The @nestjs/testing package provides a Test class that creates a lightweight TestingModule to compile, configure, and execute unit and end-to-end tests against any NestJS component while preserving dependency injection behavior.

NestJS ships with a dedicated testing package that makes it straightforward to test NestJS applications using the same dependency injection container as production code. The package lives in the packages/testing directory of the nestjs/nest repository and exports a Test utility that mirrors the framework's production DI container. This architecture allows you to test providers exactly as they run in production while substituting dependencies with mocks.

Core Testing Components in @nestjs/testing

The testing package is built around several specialized classes that handle different testing scenarios. Understanding these components helps you choose the right abstraction for your test suite.

TestingModuleBuilder and Test Factory

In packages/testing/testing-module.ts, the Test namespace exposes the createTestingModule static method. This method returns a TestingModuleBuilder (implemented in packages/testing/testing-module.builder.ts), which provides a fluent API for configuring test modules. You can import modules, declare providers, and override dependencies using methods like overrideProvider(), overrideGuard(), and overrideInterceptor().

TestingInstanceLoader for E2E Scenarios

For end-to-end testing, the TestingModule provides createNestApplication(), which internally uses TestingInstanceLoader from packages/testing/testing-instance-loader.ts. This loader spins up a minimal HTTP server that registers only the imported modules, creating an isolated environment for HTTP-level assertions.

Unit Testing Services with Mocked Dependencies

Unit tests isolate a single class while mocking its collaborators. The overrideProvider() method lets you replace real implementations with test doubles without modifying source code.

// cats.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatsService } from './cats.service';
import { CatsRepository } from './cats.repository';

describe('CatsService', () => {
  let service: CatsService;
  let repo: CatsRepository;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [CatsService, CatsRepository],
    })
      .overrideProvider(CatsRepository)
      .useValue({
        findAll: jest.fn().mockResolvedValue(['Tom', 'Kitty']),
      })
      .compile();

    service = module.get<CatsService>(CatsService);
    repo = module.get<CatsRepository>(CatsRepository);
  });

  it('should return a list of cats', async () => {
    const cats = await service.findAll();
    expect(cats).toEqual(['Tom', 'Kitty']);
    expect(repo.findAll).toHaveBeenCalled();
  });
});

The compile() method finalizes the module, resolves the dependency graph, and returns a TestingModule instance. The module.get<T>(Token) method retrieves the concrete instance from the test container, respecting NestJS's injection scopes and lifecycle hooks.

End-to-End Testing with HTTP Servers

End-to-end tests verify complete request/response cycles. The createNestApplication() method returns an INestApplication compatible with HTTP testing libraries like supertest.

// cats.controller.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { CatsModule } from '../src/cats.module';

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

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [CatsModule],
    }).compile();

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

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

  it('/GET cats', () => {
    return request(app.getHttpServer())
      .get('/cats')
      .expect(200)
      .expect(['Tom', 'Kitty']);
  });
});

Under the hood, createNestApplication() uses TestingInstanceLoader to bootstrap a standard Nest application that only includes the modules specified in your test configuration. Always close the application after tests using await app.close() to prevent open handles from hanging your test runner.

Overriding Guards, Interceptors, and Pipes

When testing controllers that use authentication or transformation logic, you often need to bypass these cross-cutting concerns. The builder exposes overrideGuard(), overrideInterceptor(), overridePipe(), and overrideFilter() methods for this purpose.

import { Test, TestingModule } from '@nestjs/testing';
import { ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '../src/auth.guard';
import { CatsController } from '../src/cats.controller';

describe('CatsController with mocked guard', () => {
  let controller: CatsController;
  const mockGuard = { canActivate: jest.fn(() => true) };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [CatsController],
    })
      .overrideGuard(AuthGuard)
      .useValue(mockGuard)
      .compile();

    controller = module.get<CatsController>(CatsController);
  });

  it('should bypass authentication', async () => {
    await controller.findAll();
    expect(mockGuard.canActivate).toHaveBeenCalled();
  });
});

These override methods are implemented in packages/testing/testing-module.builder.ts and return the builder instance, enabling method chaining for complex test configurations.

Lightweight Provider Testing with TestingInjector

For scenarios where you only need to resolve a single provider without compiling a full module, TestingInjector from packages/testing/testing-injector.ts provides a minimal container.

import { TestingInjector } from '@nestjs/testing';
import { CatsService } from '../src/cats.service';
import { CatsRepository } from '../src/cats.repository';

const injector = new TestingInjector({
  providers: [CatsService, CatsRepository],
});

const service = injector.get(CatsService);

The TestingInjector respects provider scopes and creates a lightweight container perfect for isolated service tests. This approach skips the standard module compilation process, making it faster for pure unit testing scenarios.

Summary

  • Test.createTestingModule in packages/testing/testing-module.ts is the entry point for building test modules that mirror production configurations.
  • Provider overrides via overrideProvider(), overrideGuard(), and similar methods let you substitute dependencies with mocks without source code modification.
  • TestingModule.compile() resolves the dependency graph and returns a container where you can retrieve instances using module.get<T>().
  • HTTP testing requires createNestApplication(), which utilizes TestingInstanceLoader to boot a minimal server accessible via app.getHttpServer().
  • TestingInjector offers a lightweight alternative for testing individual providers without full module compilation.
  • Always call await app.close() after end-to-end tests to prevent resource leaks.

Frequently Asked Questions

How do I mock a provider in NestJS unit tests?

Use the overrideProvider() method on the TestingModuleBuilder. Chain .useValue(), .useClass(), or .useFactory() to substitute the real implementation with a mock. After calling compile(), the testing container will inject your mock wherever that token is requested.

What is the difference between unit and end-to-end testing in NestJS?

Unit tests isolate individual providers using Test.createTestingModule and typically mock all external dependencies. End-to-end tests use module.createNestApplication() to boot an actual HTTP server and verify complete request/response cycles, often using supertest to simulate client requests.

How do I override a Guard for testing protected routes?

Call overrideGuard(AuthGuard).useValue(mockGuard) on the builder before compiling the module. This replaces the guard's implementation for that specific test, allowing you to bypass authentication logic or force specific authorization outcomes.

Can I use the NestJS testing package with test runners other than Jest?

Yes. The @nestjs/testing package returns standard Promises and is compatible with any JavaScript test runner that supports async/await. While Jest is the default in NestJS projects, you can use Vitest, Mocha, or Tape with the same TestingModule API.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →