CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jest-mock

Comprehensive mock function library providing sophisticated mocking, spying, and property replacement capabilities for JavaScript and TypeScript testing

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

spy-operations.mddocs/

Spy Operations

Non-invasive spying and property replacement capabilities for observing and temporarily modifying existing objects without permanent changes.

Capabilities

Method Spying

Creates spies on existing object methods to track calls while preserving original behavior.

/**
 * Creates a spy on an object method
 * @param object - Target object containing the method
 * @param methodKey - Name of the method to spy on
 * @returns MockInstance for the spied method
 */
function spyOn<T extends object, K extends MethodLikeKeys<T>>(
  object: T,
  methodKey: K
): Spied<Required<T>[K]>;

/**
 * Creates a spy on a property accessor (getter or setter)
 * @param object - Target object containing the property
 * @param propertyKey - Name of the property to spy on  
 * @param accessType - Whether to spy on 'get' or 'set' accessor
 * @returns MockInstance for the property accessor
 */
function spyOn<T extends object, K extends PropertyLikeKeys<T>, A extends 'get' | 'set'>(
  object: T,
  propertyKey: K,
  accessType: A
): A extends 'get' ? SpiedGetter<Required<T>[K]> : SpiedSetter<Required<T>[K]>;

type MethodLikeKeys<T> = keyof {
  [K in keyof T as Required<T>[K] extends FunctionLike ? K : never]: T[K];
};

type PropertyLikeKeys<T> = Exclude<keyof T, ConstructorLikeKeys<T> | MethodLikeKeys<T>>;

type Spied<T extends ClassLike | FunctionLike> = T extends ClassLike
  ? SpiedClass<T>
  : T extends FunctionLike
    ? SpiedFunction<T>
    : never;

type SpiedFunction<T extends FunctionLike = UnknownFunction> = MockInstance<(...args: Parameters<T>) => ReturnType<T>>;
type SpiedClass<T extends ClassLike = UnknownClass> = MockInstance<(...args: ConstructorParameters<T>) => InstanceType<T>>;
type SpiedGetter<T> = MockInstance<() => T>;
type SpiedSetter<T> = MockInstance<(arg: T) => void>;

Usage Examples:

import { spyOn } from "jest-mock";

const calculator = {
  add: (a: number, b: number) => a + b,
  subtract: (a: number, b: number) => a - b,
  history: [] as string[]
};

// Spy on a method
const addSpy = spyOn(calculator, 'add');
const result = calculator.add(2, 3);
console.log(result); // 5 (original behavior preserved)
console.log(addSpy.mock.calls); // [[2, 3]]

// Override spy behavior
addSpy.mockReturnValue(42);
console.log(calculator.add(1, 1)); // 42

// Restore original behavior
addSpy.mockRestore();
console.log(calculator.add(1, 1)); // 2

Property Spying

Creates spies on property getters and setters.

/**
 * Spy on property getter
 */
spyOn(object, propertyKey, 'get'): SpiedGetter<PropertyType>;

/**
 * Spy on property setter  
 */
spyOn(object, propertyKey, 'set'): SpiedSetter<PropertyType>;

Usage Examples:

import { spyOn } from "jest-mock";

const config = {
  _apiUrl: 'https://api.example.com',
  get apiUrl() { return this._apiUrl; },
  set apiUrl(value: string) { this._apiUrl = value; }
};

// Spy on getter
const getterSpy = spyOn(config, 'apiUrl', 'get');
console.log(config.apiUrl); // 'https://api.example.com'
console.log(getterSpy.mock.calls); // [[]]

// Override getter
getterSpy.mockReturnValue('https://test.example.com');
console.log(config.apiUrl); // 'https://test.example.com'

// Spy on setter
const setterSpy = spyOn(config, 'apiUrl', 'set');
config.apiUrl = 'https://new.example.com';
console.log(setterSpy.mock.calls); // [['https://new.example.com']]

Property Replacement

Temporarily replaces object properties with new values, providing restoration capabilities.

/**
 * Temporarily replaces an object property with a new value
 * @param object - Target object containing the property
 * @param propertyKey - Name of the property to replace
 * @param value - New value for the property
 * @returns Replaced instance with control methods
 */
function replaceProperty<T extends object, K extends keyof T>(
  object: T,
  propertyKey: K,
  value: T[K]
): Replaced<T[K]>;

interface Replaced<T = unknown> {
  /** Restore property to its original value */
  restore(): void;
  /** Change the property to a new value */
  replaceValue(value: T): this;
}

Usage Examples:

import { replaceProperty } from "jest-mock";

const config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3
};

// Replace a property
const replaced = replaceProperty(config, 'apiUrl', 'https://test.example.com');
console.log(config.apiUrl); // 'https://test.example.com'

// Change to another value
replaced.replaceValue('https://staging.example.com');
console.log(config.apiUrl); // 'https://staging.example.com'

// Restore original value
replaced.restore();
console.log(config.apiUrl); // 'https://api.example.com'

// Replace multiple properties
const timeoutReplaced = replaceProperty(config, 'timeout', 1000);
const retriesReplaced = replaceProperty(config, 'retries', 1);

// Cleanup
timeoutReplaced.restore();
retriesReplaced.restore();

Class Constructor Spying

Spy on class constructors to track instantiation.

class MyClass {
  constructor(public value: string) {}
  method() { return this.value; }
}

// Spy on constructor
const ConstructorSpy = spyOn(MyClass.prototype, 'constructor');
const instance = new MyClass('test');
console.log(ConstructorSpy.mock.calls); // [['test']]

Error Handling

Spy operations validate targets and throw descriptive errors for invalid usage:

// Throws TypeError: Cannot spy on a primitive value
spyOn('not an object', 'method');

// Throws Error: Property 'nonexistent' does not exist  
spyOn(obj, 'nonexistent');

// Throws TypeError: Cannot spy on property because it is not a function
spyOn(obj, 'stringProperty');

// Throws Error: Property is not declared configurable
const nonConfigurable = {};
Object.defineProperty(nonConfigurable, 'prop', { configurable: false, value: 'test' });
replaceProperty(nonConfigurable, 'prop', 'new');

Advanced Usage

Spy Restoration Patterns

import { spyOn } from "jest-mock";

// Individual restoration
const spy1 = spyOn(obj, 'method1');
const spy2 = spyOn(obj, 'method2');
spy1.mockRestore();
spy2.mockRestore();

// Using ModuleMocker for bulk restoration
import { ModuleMocker } from "jest-mock";
const mocker = new ModuleMocker(globalThis);
const spy3 = mocker.spyOn(obj, 'method3');
const spy4 = mocker.spyOn(obj, 'method4');
mocker.restoreAllMocks(); // Restores all spies created by this mocker

Spy Chaining

import { spyOn } from "jest-mock";

const obj = { 
  getValue: () => 42,
  chainable: function() { return this; }
};

// Chain spy operations
spyOn(obj, 'getValue')
  .mockReturnValue(100)
  .mockName('getValue spy');

spyOn(obj, 'chainable')
  .mockReturnThis()
  .mockName('chainable spy');

Types

type UnknownFunction = (...args: Array<unknown>) => unknown;
type UnknownClass = new (...args: Array<unknown>) => unknown;
type FunctionLike = (...args: any) => any;
type ClassLike = new (...args: any) => any;

type ConstructorLikeKeys<T> = keyof {
  [K in keyof T as Required<T>[K] extends ClassLike ? K : never]: T[K];
};

docs

advanced-mock-generation.md

index.md

mock-functions.md

spy-operations.md

type-system.md

tile.json