Type safe mocking extensions for jest
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Mock management utilities for clearing, resetting, and configuring mock behavior globally.
Recursively clears all mock calls and instances while preserving configured expectations and return values.
/**
* Recursively clears all mock calls and instances
* @param mock - Mock proxy to clear (can be nested)
*/
function mockClear(mock: MockProxy<any>): void;Usage Examples:
import { mock, mockClear, MockProxy } from "jest-mock-extended";
interface UserService {
getUser: (id: string) => Promise<User>;
updateUser: (id: string, data: UserData) => Promise<User>;
nested: {
cache: {
get: (key: string) => any;
set: (key: string, value: any) => void;
};
};
}
describe("UserService tests", () => {
let userService: MockProxy<UserService>;
beforeEach(() => {
userService = mock<UserService>();
// Set up expectations
userService.getUser.calledWith("123").mockResolvedValue({ id: "123", name: "John" });
userService.nested.cache.get.calledWith("user:123").mockReturnValue({ cached: true });
});
afterEach(() => {
// Clear all calls but keep expectations
mockClear(userService);
});
test("first test", async () => {
await userService.getUser("123");
userService.nested.cache.get("user:123");
expect(userService.getUser).toHaveBeenCalledTimes(1);
expect(userService.nested.cache.get).toHaveBeenCalledTimes(1);
});
test("second test", async () => {
// Calls were cleared, but expectations remain
expect(userService.getUser).toHaveBeenCalledTimes(0);
expect(userService.nested.cache.get).toHaveBeenCalledTimes(0);
// Expectations still work
const result = await userService.getUser("123");
expect(result).toEqual({ id: "123", name: "John" });
});
});Recursively resets all mock calls, instances, and return values, completely clearing all mock state.
/**
* Recursively resets all mock calls, instances, and return values
* @param mock - Mock proxy to reset (can be nested)
*/
function mockReset(mock: MockProxy<any>): void;Usage Examples:
import { mock, mockReset, MockProxy } from "jest-mock-extended";
interface ApiClient {
request: (method: string, url: string) => Promise<any>;
auth: {
login: (credentials: LoginData) => Promise<AuthToken>;
logout: () => Promise<void>;
};
}
describe("ApiClient tests", () => {
let apiClient: MockProxy<ApiClient>;
beforeEach(() => {
apiClient = mock<ApiClient>();
});
test("test with fresh mock", () => {
// Set up expectations for this test
apiClient.request.calledWith("GET", "/users").mockResolvedValue([]);
apiClient.auth.login.mockResolvedValue({ token: "abc123" });
// Use the mock
expect(apiClient.request("GET", "/users")).resolves.toEqual([]);
expect(apiClient.auth.login({ username: "user", password: "pass" }))
.resolves.toEqual({ token: "abc123" });
});
test("test with completely reset mock", () => {
// Set up some expectations
apiClient.request.mockResolvedValue("initial");
apiClient.auth.logout.mockResolvedValue(undefined);
// Use the mock
expect(apiClient.request("POST", "/data")).resolves.toBe("initial");
// Completely reset the mock - clears all calls and expectations
mockReset(apiClient);
// Mock is now completely clean
expect(apiClient.request).toHaveBeenCalledTimes(0);
expect(apiClient.auth.logout).toHaveBeenCalledTimes(0);
// Need to set up new expectations
apiClient.request.calledWith("GET", "/fresh").mockResolvedValue("fresh data");
expect(apiClient.request("GET", "/fresh")).resolves.toBe("fresh data");
});
});Creates a simple stub object that returns jest.fn() for any property access, useful for basic mocking scenarios.
/**
* Creates a stub that returns jest.fn() for any property access
* @returns Stub object with dynamic jest.fn() properties
*/
function stub<T extends object>(): T;Usage Examples:
import { stub } from "jest-mock-extended";
interface Logger {
info: (message: string) => void;
warn: (message: string) => void;
error: (message: string) => void;
debug: (message: string) => void;
}
// Create a simple stub
const logger = stub<Logger>();
// All properties return jest.fn() automatically
logger.info("test message");
logger.error("error message");
// Can set up expectations like regular jest.fn()
logger.warn.mockImplementation((msg) => console.log(`WARN: ${msg}`));
// Verify calls
expect(logger.info).toHaveBeenCalledWith("test message");
expect(logger.error).toHaveBeenCalledWith("error message");
// Example usage in dependency injection
class UserService {
constructor(private logger: Logger) {}
createUser(userData: any) {
this.logger.info("Creating user");
// ... user creation logic
this.logger.info("User created successfully");
}
}
const userService = new UserService(stub<Logger>());
userService.createUser({ name: "John" });
// Verify logging calls were made
expect(userService['logger'].info).toHaveBeenCalledTimes(2);System for configuring global mock behavior across your entire test suite.
/**
* Global configuration interface for mock behavior
*/
interface GlobalConfig {
/** Properties to ignore when creating mock proxies */
ignoreProps?: ProxiedProperty[];
}
/**
* Configuration object for global mock settings
*/
const JestMockExtended: {
/** Default configuration object */
DEFAULT_CONFIG: GlobalConfig;
/**
* Configure global mock behavior
* @param config - Configuration options to apply globally
*/
configure: (config: GlobalConfig) => void;
/** Reset configuration to default values */
resetConfig: () => void;
};
type ProxiedProperty = string | number | symbol;Usage Examples:
import { JestMockExtended, mock } from "jest-mock-extended";
// Default configuration includes ignoring 'then' property for promise handling
interface AsyncService {
fetchData: () => Promise<any>;
processData: (data: any) => Promise<void>;
}
// Configure global settings before tests
beforeAll(() => {
JestMockExtended.configure({
ignoreProps: ['then', 'catch', 'finally', 'constructor']
});
});
afterAll(() => {
// Reset to defaults after tests
JestMockExtended.resetConfig();
});
test("mock with global config", () => {
const service = mock<AsyncService>();
// 'then' property is ignored and returns undefined
expect(service.then).toBeUndefined();
// Regular mocking still works
service.fetchData.mockResolvedValue({ data: "test" });
expect(service.fetchData()).resolves.toEqual({ data: "test" });
});
// Example: Ignoring specific framework properties
interface ReactComponent {
render: () => JSX.Element;
componentDidMount: () => void;
setState: (state: any) => void;
// React internals that shouldn't be mocked
_reactInternalFiber?: any;
_reactInternalInstance?: any;
}
beforeEach(() => {
JestMockExtended.configure({
ignoreProps: [
'then', // Default for promises
'_reactInternalFiber',
'_reactInternalInstance',
'$$typeof'
]
});
});
test("React component mock", () => {
const component = mock<ReactComponent>();
// Internal React properties are ignored
expect(component._reactInternalFiber).toBeUndefined();
expect(component._reactInternalInstance).toBeUndefined();
// Regular component methods work
component.render.mockReturnValue(<div>Test</div>);
component.componentDidMount.mockImplementation(() => {});
expect(component.render()).toEqual(<div>Test</div>);
});
// Temporary configuration changes
test("temporary config override", () => {
// Save current config
const originalConfig = JestMockExtended.DEFAULT_CONFIG;
// Apply temporary config
JestMockExtended.configure({
ignoreProps: ['customProperty', 'tempProp']
});
const testMock = mock<{ customProperty: string; normalProp: string }>();
expect(testMock.customProperty).toBeUndefined();
expect(typeof testMock.normalProp).toBe('function'); // Mock function created
// Restore original config
JestMockExtended.resetConfig();
});