Comprehensive testing utilities for NestJS applications with dependency injection testing capabilities
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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;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();
});
});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();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();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 factoryHandle 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();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);
};// 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;
};// 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);
};import { InjectionToken } from "@nestjs/common";
type MockFactory = (token?: InjectionToken) => any;