CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nestjs--testing

Comprehensive testing utilities for NestJS applications with dependency injection testing capabilities

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

provider-overriding.mddocs/

Provider Overriding

System for replacing providers, guards, pipes, filters, and interceptors with test doubles. This enables precise control over dependencies during testing, allowing developers to isolate components and create predictable test scenarios.

Capabilities

Override Methods

The TestingModuleBuilder provides specific methods for overriding different types of NestJS components.

class TestingModuleBuilder {
  /**
   * Override a provider with a test double
   * @param typeOrToken - The provider class or injection token to override
   * @returns OverrideBy interface for specifying the replacement
   */
  overrideProvider<T = any>(typeOrToken: T): OverrideBy;

  /**
   * Override a pipe with a test double
   * @param typeOrToken - The pipe class or injection token to override
   * @returns OverrideBy interface for specifying the replacement
   */
  overridePipe<T = any>(typeOrToken: T): OverrideBy;

  /**
   * Override a guard with a test double
   * @param typeOrToken - The guard class or injection token to override
   * @returns OverrideBy interface for specifying the replacement
   */
  overrideGuard<T = any>(typeOrToken: T): OverrideBy;

  /**
   * Override a filter with a test double
   * @param typeOrToken - The filter class or injection token to override
   * @returns OverrideBy interface for specifying the replacement
   */
  overrideFilter<T = any>(typeOrToken: T): OverrideBy;

  /**
   * Override an interceptor with a test double
   * @param typeOrToken - The interceptor class or injection token to override
   * @returns OverrideBy interface for specifying the replacement
   */
  overrideInterceptor<T = any>(typeOrToken: T): OverrideBy;
}

OverrideBy Interface

Interface providing methods to specify how a component should be overridden.

/**
 * Interface for specifying component override strategies
 */
interface OverrideBy {
  /**
   * Replace with a specific value or instance
   * @param value - The value to use as replacement
   * @returns TestingModuleBuilder for method chaining
   */
  useValue(value: any): TestingModuleBuilder;

  /**
   * Replace with a factory function result
   * @param options - Factory configuration including function and dependencies
   * @returns TestingModuleBuilder for method chaining
   */
  useFactory(options: OverrideByFactoryOptions): TestingModuleBuilder;

  /**
   * Replace with an instance of a different class
   * @param metatype - The class to instantiate as replacement
   * @returns TestingModuleBuilder for method chaining
   */
  useClass(metatype: any): TestingModuleBuilder;
}

/**
 * Configuration options for factory-based overrides
 */
interface OverrideByFactoryOptions {
  /**
   * Factory function that creates the replacement instance
   */
  factory: (...args: any[]) => any;
  /**
   * Optional array of dependencies to inject into the factory
   */
  inject?: any[];
}

Usage Examples:

import { Test, TestingModule } from "@nestjs/testing";
import { UsersService } from "./users.service";
import { DatabaseService } from "./database.service";
import { AuthGuard } from "./auth.guard";
import { LoggingInterceptor } from "./logging.interceptor";

describe("Provider Overriding", () => {
  let module: TestingModule;

  beforeEach(async () => {
    // Override with useValue
    const mockDatabaseService = {
      findUser: jest.fn().mockResolvedValue({ id: 1, name: "Test User" }),
      saveUser: jest.fn().mockResolvedValue({ id: 1 }),
    };

    module = await Test.createTestingModule({
      providers: [UsersService, DatabaseService],
      controllers: [UsersController],
    })
      .overrideProvider(DatabaseService)
      .useValue(mockDatabaseService)
      .compile();
  });

  it("should use mocked database service", async () => {
    const usersService = module.get<UsersService>(UsersService);
    const user = await usersService.findUser(1);
    expect(user.name).toBe("Test User");
  });
});

// Override with useClass
class MockAuthGuard {
  canActivate() {
    return true;
  }
}

const moduleWithClassOverride = await Test.createTestingModule({
  providers: [UsersService],
  controllers: [UsersController],
})
  .overrideGuard(AuthGuard)
  .useClass(MockAuthGuard)
  .compile();

// Override with useFactory
const moduleWithFactory = await Test.createTestingModule({
  providers: [UsersService, DatabaseService, ConfigService],
})
  .overrideProvider(DatabaseService)
  .useFactory({
    factory: (config: ConfigService) => ({
      findUser: jest.fn(),
      connection: config.getDatabaseUrl(),
    }),
    inject: [ConfigService],
  })
  .compile();

// Override interceptor
const moduleWithInterceptor = await Test.createTestingModule({
  providers: [UsersService],
  controllers: [UsersController],
})
  .overrideInterceptor(LoggingInterceptor)
  .useValue({
    intercept: jest.fn((context, next) => next.handle()),
  })
  .compile();

Advanced Override Patterns

Partial Mocking:

// Override with partial implementation
const partialMockService = {
  findUser: jest.fn().mockResolvedValue({ id: 1, name: "Test" }),
  // Other methods will use original implementation or throw
};

const module = await Test.createTestingModule({
  providers: [UsersService, DatabaseService],
})
  .overrideProvider(DatabaseService)
  .useValue(partialMockService)
  .compile();

Spy Integration:

// Create spy-enabled mock
const databaseServiceSpy = {
  findUser: jest.fn(),
  saveUser: jest.fn(),
  deleteUser: jest.fn(),
};

const module = await Test.createTestingModule({
  providers: [UsersService, DatabaseService],
})
  .overrideProvider(DatabaseService)
  .useValue(databaseServiceSpy)
  .compile();

// Verify interactions
expect(databaseServiceSpy.findUser).toHaveBeenCalledWith(1);

Token-based Overrides:

const DATABASE_TOKEN = Symbol("DATABASE_TOKEN");

const module = await Test.createTestingModule({
  providers: [
    UsersService,
    {
      provide: DATABASE_TOKEN,
      useClass: DatabaseService,
    },
  ],
})
  .overrideProvider(DATABASE_TOKEN)
  .useValue(mockDatabase)
  .compile();

Error Handling

Common override scenarios and error handling:

// Handle missing dependencies
try {
  const module = await Test.createTestingModule({
    providers: [ServiceWithDependencies],
  })
    .overrideProvider(MissingDependency)
    .useValue(mockDependency)
    .compile();
} catch (error) {
  // Handle compilation errors
}

// Override non-existent provider (will be ignored)
const module = await Test.createTestingModule({
  providers: [UsersService],
})
  .overrideProvider(NonExistentService) // This won't cause an error
  .useValue(mockService)
  .compile();

Types

import { TestingModuleBuilder } from "./testing-module.builder";

interface OverrideBy {
  useValue: (value: any) => TestingModuleBuilder;
  useFactory: (options: OverrideByFactoryOptions) => TestingModuleBuilder;
  useClass: (metatype: any) => TestingModuleBuilder;
}

interface OverrideByFactoryOptions {
  factory: (...args: any[]) => any;
  inject?: any[];
}

docs

application-testing.md

index.md

mock-integration.md

module-building.md

module-overriding.md

provider-overriding.md

tile.json