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

mock-integration.mddocs/

Mock Integration

Global mocking system with automatic fallback for unresolved dependencies. This system integrates with popular mocking libraries to provide comprehensive test isolation and automatic mock generation for missing dependencies.

Capabilities

Mock Factory Integration

The TestingModuleBuilder supports global mock factories that automatically create mocks for unresolved dependencies.

class TestingModuleBuilder {
  /**
   * Set a global mock factory for automatic dependency mocking
   * @param mocker - Function that creates mocks based on injection tokens
   * @returns TestingModuleBuilder for method chaining
   */
  useMocker(mocker: MockFactory): TestingModuleBuilder;
}

/**
 * Function type for creating mocks based on injection tokens
 * @param token - The injection token for the dependency that needs mocking
 * @returns Mock implementation for the dependency
 */
type MockFactory = (token?: InjectionToken) => any;

Automatic Mock Resolution

The testing system automatically creates mocks for missing dependencies when a MockFactory is provided. When the dependency injection system cannot resolve a dependency, it falls back to using the mock factory to create a replacement.

Usage Examples:

import { Test, TestingModule } from "@nestjs/testing";
import { createMock } from "@golevelup/ts-jest";
import { UsersService } from "./users.service";
import { DatabaseService } from "./database.service";
import { EmailService } from "./email.service";

describe("Mock Integration", () => {
  let module: TestingModule;

  beforeEach(async () => {
    // Using @golevelup/ts-jest for automatic mocking
    module = await Test.createTestingModule({
      providers: [UsersService],
      // Note: DatabaseService and EmailService are not provided
    })
      .useMocker(createMock)
      .compile();
  });

  it("should automatically mock missing dependencies", async () => {
    const usersService = module.get<UsersService>(UsersService);
    
    // DatabaseService is automatically mocked
    const result = await usersService.getUsers();
    expect(result).toBeDefined();
  });
});

Custom Mock Factory

Create custom mock factories for specific testing needs:

// Simple mock factory
const simpleMockFactory = (token: any): any => {
  if (typeof token === "function") {
    // For class constructors, create an object with mocked methods
    const mockObj = {};
    const prototype = token.prototype;
    
    if (prototype) {
      Object.getOwnPropertyNames(prototype).forEach((method) => {
        if (method !== "constructor" && typeof prototype[method] === "function") {
          mockObj[method] = jest.fn();
        }
      });
    }
    return mockObj;
  }
  
  // For tokens/strings, return a simple mock
  return jest.fn();
};

// Advanced mock factory with type-aware mocking
const advancedMockFactory = (token: any): any => {
  const tokenName = token?.name || token?.toString();
  
  switch (tokenName) {
    case "DatabaseService":
      return {
        findUser: jest.fn().mockResolvedValue({ id: 1, name: "Mock User" }),
        saveUser: jest.fn().mockResolvedValue({ id: 1 }),
        deleteUser: jest.fn().mockResolvedValue(true),
      };
      
    case "EmailService":
      return {
        sendEmail: jest.fn().mockResolvedValue({ sent: true, id: "mock-id" }),
        validateEmail: jest.fn().mockReturnValue(true),
      };
      
    case "Logger":
      return {
        log: jest.fn(),
        error: jest.fn(),
        warn: jest.fn(),
        debug: jest.fn(),
      };
      
    default:
      // Fallback to generic mock
      return createMock(token);
  }
};

const moduleWithCustomMocking = await Test.createTestingModule({
  providers: [ComplexService],
})
  .useMocker(advancedMockFactory)
  .compile();

Integration with Popular Mocking Libraries

Jest Auto-Mock:

import { Test } from "@nestjs/testing";

// Simple Jest-based mock factory
const jestMockFactory = (token: any) => {
  if (typeof token === "function") {
    const mockClass = jest.fn(() => ({}));
    mockClass.prototype = Object.create(token.prototype);
    return new mockClass();
  }
  return jest.fn();
};

const module = await Test.createTestingModule({
  providers: [ServiceUnderTest],
})
  .useMocker(jestMockFactory)
  .compile();

Sinon Integration:

import * as sinon from "sinon";

const sinonMockFactory = (token: any) => {
  if (typeof token === "function") {
    const mockObj = {};
    const prototype = token.prototype;
    
    Object.getOwnPropertyNames(prototype || {}).forEach((method) => {
      if (method !== "constructor") {
        mockObj[method] = sinon.stub();
      }
    });
    
    return mockObj;
  }
  return sinon.stub();
};

const module = await Test.createTestingModule({
  providers: [ServiceUnderTest],
})
  .useMocker(sinonMockFactory)
  .compile();

ts-mockito Integration:

import { mock, instance } from "ts-mockito";

const tsMockitoFactory = (token: any) => {
  if (typeof token === "function") {
    const mockedClass = mock(token);
    return instance(mockedClass);
  }
  return mock();
};

const module = await Test.createTestingModule({
  providers: [ServiceUnderTest],
})
  .useMocker(tsMockitoFactory)
  .compile();

Selective Mocking

Combine automatic mocking with specific overrides:

const module = await Test.createTestingModule({
  providers: [UsersService, EmailService], // EmailService is provided
})
  .overrideProvider(EmailService)
  .useValue({
    sendEmail: jest.fn().mockResolvedValue({ sent: true }),
  })
  .useMocker(createMock) // Only mocks dependencies not explicitly provided/overridden
  .compile();

// EmailService uses the explicit override
// Other missing dependencies use the mock factory

Mock Factory Error Handling

Handle cases where mocking fails or dependencies can't be created:

const safeMockFactory = (token: any): any => {
  try {
    // Attempt to create a specific mock
    if (token?.name === "ProblematicService") {
      return {
        method1: jest.fn().mockRejectedValue(new Error("Service unavailable")),
        method2: jest.fn().mockReturnValue(null),
      };
    }
    
    // Default mocking logic
    return createMock(token);
  } catch (error) {
    console.warn(`Failed to mock ${token?.name || token}:`, error.message);
    
    // Return a minimal mock as fallback
    return {
      [Symbol.toStringTag]: `Mock<${token?.name || "Unknown"}>`,
    };
  }
};

const module = await Test.createTestingModule({
  providers: [ServiceWithProblematicDependency],
})
  .useMocker(safeMockFactory)
  .compile();

Testing with Partial Mocks

Create partial mocks that preserve some original functionality:

import { UsersService } from "./users.service";

const partialMockFactory = (token: any): any => {
  if (token === UsersService) {
    const originalService = new UsersService({} as any);
    
    return {
      ...originalService,
      // Override specific methods
      findUser: jest.fn().mockResolvedValue({ id: 1, name: "Test User" }),
      // Keep other methods from original implementation
    };
  }
  
  return createMock(token);
};

Integration Patterns

Async Mock Factories

// Mock factory that handles async initialization
const asyncMockFactory = (token: any): any => {
  const mockObj = createMock(token);
  
  // Ensure async methods return promises
  Object.keys(mockObj).forEach((key) => {
    if (typeof mockObj[key] === "function") {
      // Make all methods async-compatible
      const originalMock = mockObj[key];
      mockObj[key] = jest.fn().mockImplementation((...args) => {
        const result = originalMock(...args);
        return Promise.resolve(result);
      });
    }
  });
  
  return mockObj;
};

Contextual Mocking

// Context-aware mock factory
const contextualMockFactory = (token: any): any => {
  const testName = expect.getState().currentTestName;
  
  if (testName?.includes("error")) {
    // Create error-throwing mocks for error tests
    return createMock(token, {
      throwError: true,
    });
  }
  
  if (testName?.includes("empty")) {
    // Create empty-result mocks
    return createMock(token, {
      returnEmpty: true,
    });
  }
  
  return createMock(token);
};

Types

import { InjectionToken } from "@nestjs/common";

type MockFactory = (token?: InjectionToken) => any;

docs

application-testing.md

index.md

mock-integration.md

module-building.md

module-overriding.md

provider-overriding.md

tile.json