New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@corez/mock

Package Overview
Dependencies
Maintainers
0
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@corez/mock

A powerful and flexible TypeScript mocking library for testing

  • 0.9.1
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
12
increased by33.33%
Maintainers
0
Weekly downloads
 
Created
Source

@corez/mock

A powerful, flexible, and type-safe mocking library for TypeScript testing.

npm version License: Apache-2.0 Node Version pnpm

Features

  • 🎯 Type Safety - Full TypeScript support with precise type inference
  • 🔄 Deep Mocking - Automatic mocking of nested objects and methods
  • 🕵️ Spy Tracking - Comprehensive call tracking and verification
  • 🎭 Multiple Mocking Styles - Support for functions, objects, and classes
  • 🔗 Inheritance Support - Proper handling of class inheritance and prototype chains
  • 🎮 Intuitive API - Clean and chainable API design
  • 🛡 Debug Support - Detailed logging for troubleshooting
  • 🔄 In-Place Mocking - Option to modify original classes with restore capability

Installation

# Using npm
npm install @corez/mock --save-dev

# Using yarn
yarn add -D @corez/mock

# Using pnpm
pnpm add -D @corez/mock

Quick Start

import {mock} from '@corez/mock';

// Mock a function
const greet = mock.fn<(name: string) => string>();
greet.mockImplementation(name => `Hello, ${name}!`);

// Mock an object
interface User {
  id: number;
  name: string;
}

interface UserService {
  getUser(id: number): Promise<User>;
  updateUser(user: User): Promise<void>;
}

const userService = mock.obj<UserService>(
  {
    getUser: async id => ({id, name: 'John'}),
    updateUser: async user => {},
  },
  {
    overrides: {
      getUser: async id => ({id, name: 'Mock User'}),
    },
  },
);

// Mock a class
class Database {
  async connect() {
    /* ... */
  }
  async query(sql: string) {
    /* ... */
  }
}

// Create a new mock class
const MockDatabase = mock.cls(Database, {
  mockStatic: true,
  preserveConstructor: true,
});

const db = new MockDatabase();
db.query.mockResolvedValue({rows: []});

Core Concepts

Mock Functions

Create standalone mock functions with full tracking capabilities:

const mockFn = mock.fn<(x: number) => number>();

// Set implementation
mockFn.mockImplementation(x => x * 2);

// Set return value
mockFn.mockReturnValue(42);

// Handle async scenarios
mockFn.mockResolvedValue('result');
mockFn.mockRejectedValue(new Error('failed'));

// Verify calls
expect(mockFn.calls.count()).toBe(1);
expect(mockFn.calls.all()[0].args).toEqual([1]);

Mock Objects

Create mock objects with automatic method tracking:

interface UserService {
  getUser(id: number): Promise<User>;
  updateUser(user: User): Promise<void>;
}

const userService = mock.obj<UserService>({
  getUser: async id => ({id, name: 'John'}),
  updateUser: async user => {},
});

// Track method calls
userService.getUser(1);
expect((userService.getUser as any).mock.calls.length).toBe(1);

// Override return values
(userService.getUser as any).mockResolvedValue({id: 1, name: 'Mock'});

// Access call information
const getUserMock = userService.getUser as MockFunction;
console.log(getUserMock.calls.count());
console.log(getUserMock.calls.all());
Advanced Object Mocking Features
  1. Prototype Chain Mocking
const service = mock.obj(originalService, {
  prototypeChain: true, // Will mock methods from entire prototype chain
});
  1. Arrow Functions vs Regular Methods The library automatically detects and handles arrow functions differently to preserve correct this binding:
class Service {
  regularMethod() {
    return this;
  }
  arrowMethod = () => this;
}
const mockedService = mock.obj(new Service());
// Both methods will maintain correct `this` binding
  1. In-Place Mocking
const service = mock.obj(originalService, {
  inPlace: true, // Modifies the original object
});

Mock Classes

Create mock classes with automatic method tracking:

class Database {
  async connect() {
    /* ... */
  }
  async query(sql: string) {
    /* ... */
  }
}

// Create a new mock class
const MockDatabase = mock.cls(Database, {
  static: true,
  preserveConstructor: true,
});

// Check if class is mocked
console.log(MockDatabase.__is_mocked__); // true
console.log(MockDatabase[IS_MOCKED]); // true

const db = new MockDatabase();
// Access call information
const queryMock = db.query as MockFunction;
console.log(queryMock.calls.count());

Factory Mocking

Create factory-based mock classes that automatically mock instances on construction:

class Database {
  async connect() {
    /* ... */
  }
  async query(sql: string) {
    /* ... */
  }
}

// Create a factory mock class
const MockDatabase = mock.factory(Database, {
  prototypeChain: true, // Mock all prototype methods
});

// Each instance is automatically mocked
const db = new MockDatabase();
expect(db.query).toHaveProperty('mock'); // true

// Mock implementation
db.query.mockResolvedValue({rows: []});

// Track calls
db.query('SELECT * FROM users');
expect(db.query).toHaveBeenCalled();

// In-place mocking
mock.factory(Database, {
  inPlace: true, // Modify original class
  prototypeChain: true,
});

// Now original class creates mocked instances
const db2 = new Database();
db2.query.mockResolvedValue({rows: []});

Key features of factory mocking:

  • Automatic instance mocking on construction
  • Proper inheritance handling using Reflect.construct
  • Support for prototype chain mocking
  • In-place modification option
  • Automatic spy tracking for all methods
  • Preserves constructor behavior

Type Definitions

The library provides comprehensive type definitions for mocked classes:

// Mock class type that includes mock markers
type ClsMock<T extends Constructor<any>> = T & {
  __is_mocked__: boolean;
  [IS_MOCKED]: true;
  new (...args: ConstructorParameters<T>): InstanceType<T>;
};

// Usage with type checking
const mockDb: ClsMock<typeof Database> = mock.cls(Database);
if (mockDb.__is_mocked__) {
  console.log('Class is mocked');
}

API Reference

Core APIs

mock.fn<T extends Fn = Fn>(): MockFunction<T>

Creates a mock function with tracking capabilities:

const mockFn = mock.fn<(x: number) => number>();

// Set implementation
mockFn.mockImplementation(x => x * 2);

// Set return value
mockFn.mockReturnValue(42);

// Handle async scenarios
mockFn.mockResolvedValue('result');
mockFn.mockRejectedValue(new Error('failed'));

// Verify calls
expect(mockFn.calls.count()).toBe(1);
expect(mockFn.calls.all()[0].args).toEqual([1]);
mock.obj<T extends object>(target: T | undefined, options?: ObjectMockOptions<T>): MockObject<T>

Creates a mock object with automatic method tracking. Available options:

interface ObjectMockOptions<T> {
  // If true, modifies the original object instead of creating a clone
  inPlace?: boolean;

  // If true, mocks methods from the entire prototype chain
  prototypeChain?: boolean;

  // Override specific properties or methods
  overrides?: Partial<T>;
}

Key features:

  • Deep cloning support using rfdc
  • Automatic handling of arrow functions vs regular methods
  • Prototype chain preservation and mocking
  • Property descriptor preservation
  • Spy tracking for all methods
mock.cls<T extends Constructor<any>>(target: T, options?: ClassMockOptions<T>): ClsMock<T>

Creates a mock class with automatic method tracking and type safety:

// Create a mock class with options
const MockDatabase = mock.cls(Database, {
  // Modify original class instead of creating new one
  inPlace: false,

  // Preserve original method implementations
  preservePrototype: true,

  // Call original constructor
  preserveConstructor: true,

  // Override specific methods
  overrides: {
    query: async () => [{id: 1}],
  },

  // Enable debug logging
  debug: false,
});

// Type checking and verification
if (MockDatabase.__is_mocked__) {
  console.log('Class is properly mocked');
}

// Access mock information
const instance = new MockDatabase();
const queryMock = instance.query as MockFunction;
console.log(queryMock.calls.count());

The ClassMockOptions interface provides fine-grained control:

interface ClassMockOptions<T> {
  // Whether to modify the original class
  inPlace?: boolean;

  // Whether to preserve original method implementations
  preservePrototype?: boolean;

  // Whether to call original constructor
  preserveConstructor?: boolean;

  // Method overrides
  overrides?: Partial<T>;

  // Enable debug logging
  debug?: boolean;
}
mock.factory<T extends Constructor<any>>(target: T, options?: ObjectMockOptions): T

Creates a factory-based mock class that automatically mocks instances on construction:

// Create a factory mock class
const MockDatabase = mock.factory(Database, {
  // Mock all prototype chain methods
  prototypeChain: true,

  // Modify original class instead of creating new one
  inPlace: false,

  // Override specific methods
  overrides: {
    query: async () => [{id: 1}],
  },

  // Enable debug logging
  debug: false,
});

// Each instance is automatically mocked
const db = new MockDatabase();

// Mock implementation
db.query.mockResolvedValue({rows: []});

// Track calls
db.query('SELECT * FROM users');
expect(db.query).toHaveBeenCalled();

The factory function provides these key features:

  • Automatic instance mocking on construction
  • Proper inheritance handling using Reflect.construct
  • Support for prototype chain mocking
  • In-place modification option
  • Automatic spy tracking for all methods
  • Preserves constructor behavior

Available options:

interface FactoryOptions extends ObjectMockOptions {
  // When true, mocks all prototype chain methods
  prototypeChain?: boolean;

  // When true, modifies original class
  inPlace?: boolean;

  // Override specific methods
  overrides?: Partial<T>;

  // Enable debug logging
  debug?: boolean;
}
mock.compose<T extends Fn>(): MockFunction<T>;
mock.compose<T extends new (...args: any[]) => any>(target: T, options?: {overrides?: DeepPartial<InstanceType<T>>} & Partial<Config>): ClsMock<T>;
mock.compose<T extends object>(target: T, options?: {overrides?: DeepPartial<T>; replace?: {[K in keyof T]?: T[K] extends Fn ? Fn : never}} & Partial<Config>): T;
mock.compose<T extends object>(partialImpl: DeepPartial<T>, options?: Partial<Config>): T;

Creates a mock from a class constructor, object, or function:

// Mock a function
const mockFn = mock.compose<(x: number) => string>();
mockFn.mockImplementation(x => x.toString());

// Mock a class
class Database {
  async query(sql: string) {
    /* ... */
  }
}

const MockDatabase = mock.compose(Database, {
  overrides: {
    query: async () => [{id: 1}],
  },
});

// Mock an object
interface Api {
  fetch(url: string): Promise<any>;
}

const api = mock.compose<Api>(
  {
    fetch: async url => ({data: []}),
  },
  {
    overrides: {
      fetch: async url => ({data: [{id: 1}]}),
    },
  },
);

// Mock with partial implementation
interface ComplexApi {
  getUsers(): Promise<User[]>;
  getUser(id: number): Promise<User>;
  createUser(user: User): Promise<void>;
}

const partialApi = mock.compose<ComplexApi>({
  getUsers: async () => [{id: 1, name: 'John'}],
  getUser: async id => ({id, name: 'John'}),
});
mock.cast<T extends object>(partial: DeepPartial<T>, options?: Partial<Config>): T

Casts a partial implementation to a complete mock:

interface CompleteApi {
  getUsers(): Promise<User[]>;
  getUser(id: number): Promise<User>;
  createUser(user: User): Promise<void>;
  updateUser(user: User): Promise<void>;
  deleteUser(id: number): Promise<void>;
}

// Only implement the methods we need
const api = mock.cast<CompleteApi>({
  getUsers: async () => [{id: 1, name: 'John'}],
  getUser: async id => ({id, name: 'John'}),
});

// All other methods will be automatically mocked
await api.createUser({id: 1, name: 'Test'}); // Works, returns undefined
await api.updateUser({id: 1, name: 'Test'}); // Works, returns undefined
await api.deleteUser(1); // Works, returns undefined

// Access call information
const createUserMock = api.createUser as MockFunction;
expect(createUserMock.calls.count()).toBe(1);
mock.replace<T extends object, K extends keyof T>(obj: T, key: K, impl: Fn, options?: Partial<Config>): void

Replaces methods while preserving original implementation:

class Service {
  async getData() {
    /* ... */
  }

  async processData(data: any) {
    /* ... */
  }
}

const service = new Service();

// Replace single method
mock.replace(service, 'getData', async () => ['mocked']);

// Verify the method was replaced
expect(await service.getData()).toEqual(['mocked']);

// Original processData method remains unchanged
expect(service.processData).toBeDefined();

// Access call information
const getDataMock = service.getData as unknown as MockFunction;
expect(getDataMock.calls.count()).toBe(1);

// Restore original implementation
mock.restore(service);

Mock Control

All mocks provide these control methods:

// Clear call history
mock.mockClear(); // Clears calls but keeps implementation

// Reset completely
mock.mockReset(); // Clears everything

// Restore original
mock.mockRestore(); // Restores original implementation

// Access state
mock.calls.all(); // Call arguments with context
mock.calls.count(); // Number of calls

Advanced Usage

Async Mocking

Handle async operations:

const api = mock.obj<Api>(
  {},
  {
    overrides: {
      fetch: async url => {
        if (url === '/users') {
          return [{id: 1}];
        }
        throw new Error('Not found');
      },
    },
  },
);

Partial Mocking

Selectively mock methods:

class UserService {
  async getUser(id: number) {
    /* ... */
  }
  async validate(user: User) {
    /* ... */
  }
}

const service = new UserService();

// Using replace for method replacement
mock.replace(service, 'getUser', async id => ({
  id,
  name: 'Mock User',
}));

// Using overrides for partial implementation
const partialService = mock.obj<UserService>(
  {},
  {
    overrides: {
      getUser: async id => ({id, name: 'Mock User'}),
    },
  },
);

Best Practices

  1. Reset Between Tests

    beforeEach(() => {
      mockFn.mockReset();
      // or
      mockObj.mockReset();
    });
    
  2. Type Safety

    // Prefer interfaces for better type inference
    interface Service {
      method(): string;
    }
    const mock = mock.obj<Service>();
    
  3. Error Handling

    // Always test error cases
    api.fetch.mockRejectedValue(new Error('Network error'));
    await expect(api.fetch()).rejects.toThrow('Network error');
    
  4. Verification

    // Verify call count and arguments
    expect(mockFn.calls.count()).toBe(1);
    expect(mockFn.calls.all()[0].args).toEqual(['expected arg']);
    
  5. Using inPlace

    // For objects: Use inPlace when you need to maintain object references
    const mockObj = mock.obj(original, {
      inPlace: true,
      overrides: {
        method: () => 'mocked',
      },
    });
    
    // For classes: Be cautious with inPlace as it affects the original class
    const MockClass = mock.cls(Original); // Creates new class by default
    
    // If you need to modify the original class:
    const ModifiedClass = mock.cls(Original, {
      inPlace: true,
      // Remember to restore if needed
    });
    

Troubleshooting

Common Issues

  1. Mock Not Tracking Calls

    • For functions: Enable trackCalls in FunctionMockOptions
    • For objects and classes: Method calls are tracked automatically
    • Ensure you're accessing the mock properties correctly (e.g., mock.calls)
  2. Type Inference Issues

    • Use explicit type parameters with interfaces
    • Define complete interfaces for complex types
    • Use as MockFunction<T> for better type support
  3. Prototype Chain Issues

    • For classes: Use preservePrototype: true (default)
    • For objects: Use prototypeChain: true to mock prototype methods (default)
    • For type casting: Use keepPrototype: true (default)
  4. inPlace Issues

    • For objects: Use inPlace: true if you need to maintain object references
    • For classes: Be aware that inPlace: true modifies the original class
    • Remember to restore mocked objects/classes if needed:
      const mock = mock.obj(original, {inPlace: true});
      // ... use mock ...
      mock.restore(); // Restore original implementation
      
    • When using inPlace with inheritance, ensure proper super calls are preserved
    • Consider using a new instance if you don't specifically need to modify the original

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

Apache-2.0 - see LICENSE for details.

Mock Options

Base Options

All mock types support these basic options:

  • debug: Enables detailed logging of mock operations (default: false)
  • asyncTimeout: Maximum time to wait for async operations in ms (default: 5000)

Function Mock Options

Options for mocking functions:

  • trackCalls: Records arguments, return values, and call count (default: false)
  • autoSpy: Automatically creates spies for all function properties (default: false)

Object Mock Options

Options for mocking objects:

  • inPlace: Controls whether to modify the original object or create a new one (default: false)
  • prototypeChain: Controls whether to perform deep cloning and mocking of nested objects and prototype chain methods (default: true)
  • overrides: Allows overriding specific properties or methods with custom implementations

Example of object mocking with inPlace:

// Create a new mock object (default behavior)
const mockObj = mock.obj(original, {
  overrides: {
    method: () => 'mocked',
  },
});

// Modify the original object
const modifiedObj = mock.obj(original, {
  inPlace: true,
  overrides: {
    method: () => 'mocked',
  },
});

// Deep mocking is enabled by default
const deepMockObj = mock.obj(original, {
  prototypeChain: true, // this is default
});

Class Mock Options

Options for mocking classes:

  • inPlace: Controls whether to modify the original class or create a new one (default: false)
  • preservePrototype: Controls whether to preserve the prototype chain (default: true)
  • preserveConstructor: Controls whether to preserve the original constructor behavior (default: true)
  • mockStatic: Controls whether to mock static class members (default: false)
  • overrides: Allows overriding specific properties or methods with custom implementations

Example of class mocking with inPlace:

// Create a new mock class (default behavior)
const MockClass = mock.cls(Original, {
  static: true,
  overrides: {
    method: () => 'mocked',
  },
});

// Modify the original class
const ModifiedClass = mock.cls(Original, {
  inPlace: true,
  static: true,
  overrides: {
    method: () => 'mocked',
  },
});

Cast Mock Options

Options for type casting and partial implementations:

  • keepPrototype: Maintains the prototype chain when casting objects (default: true)

Usage Examples

Basic Function Mocking

import {mock} from '@corez/mock';

// Create a mock function
const mockFn = mock.fn<() => string>();
mockFn.mockReturnValue('hello');

// Track calls
expect(mockFn()).toBe('hello');
expect(mockFn.mock.calls.length).toBe(1);

Object Mocking

import {mock} from '@corez/mock';

interface User {
  name: string;
  getId(): number;
}

// Create a mock object
const mockUser = mock.obj<User>({
  name: 'Test User',
  getId: () => 1,
});

// Override methods
mockUser.getId.mockReturnValue(2);

Class Mocking

import {mock} from '@corez/mock';

class Database {
  connect() {
    /* ... */
  }
  query(sql: string) {
    /* ... */
  }
}

// Create a mock class
const MockDatabase = mock.cls(Database, {
  mockStatic: true,
  preserveConstructor: true,
});

const db = new MockDatabase();
db.query.mockResolvedValue({rows: []});

Type Casting

import {mock} from '@corez/mock';

interface ComplexType {
  data: string;
  process(): Promise<void>;
}

// Cast partial implementation to full mock
const partial = {data: 'test'};
const mockObj = mock.cast<ComplexType>(partial);

Mock Composition

Compose Function Options

The compose function provides a unified interface for creating mocks of different types (classes, objects, and functions). It uses a sophisticated type system to provide appropriate options based on the target type.

Options Structure
type ComposeOptions<T> = BaseMockOptions &
  (T extends new (...args: any[]) => any
    ? ClassMockOptions<InstanceType<T>>
    : ObjectMockOptions<T> & {
        replace?: {
          [K in keyof T]?: T[K] extends Fn ? Fn : never;
        };
      });
Design Decisions
  1. Replace vs Override

    • replace is specifically designed for object mocking, allowing direct method replacement
    • override is used in class mocking for overriding prototype methods
    • While they may seem similar, they serve different purposes and contexts
  2. Type Safety

    • The options type automatically adapts based on the target type
    • Class mocks receive ClassMockOptions
    • Object mocks receive ObjectMockOptions with additional replace capability
    • This ensures type safety while maintaining flexibility
  3. Usage Examples

// Class mocking
class Service {
  getData(): string {
    return 'data';
  }
}
const mockService = compose(Service, {
  override: {
    getData: () => 'mocked',
  },
});

// Object mocking with replace
const obj = {
  method: () => 'original',
};
const mockObj = compose(obj, {
  replace: {
    method: () => 'replaced',
  },
});
  1. Best Practices
    • Use override for class method mocking
    • Use replace for direct object method replacement
    • Consider the scope and context when choosing between them
    • Avoid mixing override and replace in the same mock

Debugging Features

Debug Options

The library provides comprehensive debugging capabilities through the debug

Keywords

FAQs

Package last updated on 12 Jan 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc