Comprehensive mock function library providing sophisticated mocking, spying, and property replacement capabilities for JavaScript and TypeScript testing
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Non-invasive spying and property replacement capabilities for observing and temporarily modifying existing objects without permanent changes.
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)); // 2Creates 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']]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();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']]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');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 mockerimport { 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');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];
};