JavaScript test spies, stubs and mocks for framework-agnostic unit testing.
—
Test spies record function calls, arguments, return values, and exceptions without changing the original function's behavior. They're perfect for verifying that functions are called correctly and for observing interactions between components.
Create spies to monitor function calls and gather interaction data.
/**
* Creates an anonymous spy function
* @returns New spy function that records calls
*/
function spy(): SinonSpy;
/**
* Wraps an existing function with spy functionality
* @param func - Function to wrap with spy
* @returns Spy wrapping the original function
*/
function spy<F extends Function>(func: F): SinonSpy<F>;
/**
* Replaces an object method with a spy
* @param object - Object containing the method
* @param method - Method name to spy on
* @returns Spy replacing the original method
*/
function spy<T>(object: T, method: keyof T): SinonSpy;Usage Examples:
import { spy } from "sinon";
// Anonymous spy
const callback = spy();
someFunction(callback);
console.log(callback.called); // true
// Spy on existing function
const originalFn = (x) => x * 2;
const spiedFn = spy(originalFn);
console.log(spiedFn(5)); // 10 (original behavior preserved)
console.log(spiedFn.calledWith(5)); // true
// Spy on object method
const user = { getName: () => "John" };
const nameSpy = spy(user, "getName");
user.getName(); // Still returns "John"
console.log(nameSpy.calledOnce); // trueSpy on object property getters and setters to monitor property access patterns.
/**
* Spy on object property accessors (getter/setter)
* @param object - Object containing the property
* @param property - Property name to spy on
* @param accessors - Array specifying which accessors to spy on
* @returns Spy for the property accessors
*/
function spy<T>(object: T, property: keyof T, accessors: ["get"] | ["set"] | ["get", "set"]): SinonSpy;Usage Examples:
import { spy } from "sinon";
const user = {
_name: "John",
get name() {
return this._name;
},
set name(value) {
this._name = value;
}
};
// Spy on getter only
const getterSpy = spy(user, "name", ["get"]);
console.log(user.name); // "John"
console.log(getterSpy.calledOnce); // true
// Spy on setter only
const setterSpy = spy(user, "name", ["set"]);
user.name = "Jane";
console.log(setterSpy.calledOnce); // true
console.log(setterSpy.calledWith("Jane")); // true
// Spy on both getter and setter
const user2 = { /* similar object */ };
const accessorSpy = spy(user2, "name", ["get", "set"]);
user2.name = "Bob"; // Records setter call
console.log(user2.name); // Records getter call
console.log(accessorSpy.callCount); // 2Properties for checking spy call state and history.
interface SinonSpy {
/** Whether the spy was called at least once */
called: boolean;
/** Number of times the spy was called */
callCount: number;
/** Whether the spy was called exactly once */
calledOnce: boolean;
/** Whether the spy was called exactly twice */
calledTwice: boolean;
/** Whether the spy was called exactly three times */
calledThrice: boolean;
/** First call object (null if never called) */
firstCall: SinonSpyCall | null;
/** Second call object (null if called less than twice) */
secondCall: SinonSpyCall | null;
/** Third call object (null if called less than three times) */
thirdCall: SinonSpyCall | null;
/** Last call object (null if never called) */
lastCall: SinonSpyCall | null;
}Methods for verifying spy calls with specific arguments.
interface SinonSpy {
/**
* Check if spy was called with specific arguments (partial match)
* @param args - Arguments to check for
* @returns Whether spy was called with these arguments
*/
calledWith(...args: any[]): boolean;
/**
* Check if spy was called with exact arguments (exact match)
* @param args - Exact arguments to check for
* @returns Whether spy was called with exactly these arguments
*/
calledWithExactly(...args: any[]): boolean;
/**
* Check if spy was called with arguments matching patterns
* @param matchers - Matcher patterns to check against
* @returns Whether spy was called with matching arguments
*/
calledWithMatch(...matchers: any[]): boolean;
/**
* Check if spy was always called with specific arguments
* @param args - Arguments that should be present in every call
* @returns Whether every call included these arguments
*/
alwaysCalledWith(...args: any[]): boolean;
/**
* Check if spy was always called with exact arguments
* @param args - Exact arguments for every call
* @returns Whether every call had exactly these arguments
*/
alwaysCalledWithExactly(...args: any[]): boolean;
/**
* Check if spy was always called with matching arguments
* @param matchers - Matcher patterns for every call
* @returns Whether every call matched these patterns
*/
alwaysCalledWithMatch(...matchers: any[]): boolean;
/**
* Check if spy was never called with specific arguments
* @param args - Arguments that should never be present
* @returns Whether spy was never called with these arguments
*/
neverCalledWith(...args: any[]): boolean;
/**
* Check if spy was never called with matching arguments
* @param matchers - Matcher patterns that should never match
* @returns Whether spy was never called with matching arguments
*/
neverCalledWithMatch(...matchers: any[]): boolean;
}Usage Examples:
const spy = sinon.spy();
spy("hello", { name: "world" });
spy("goodbye", { name: "world" });
console.log(spy.calledWith("hello")); // true
console.log(spy.calledWithExactly("hello", { name: "world" })); // true
console.log(spy.alwaysCalledWith({ name: "world" })); // true
console.log(spy.neverCalledWith("invalid")); // true
// Using matchers
import { match } from "sinon";
console.log(spy.calledWithMatch("hello", match.object)); // trueMethods for verifying the this context of spy calls.
interface SinonSpy {
/**
* Check if spy was called with specific `this` context
* @param thisValue - The expected `this` value
* @returns Whether spy was called with this context
*/
calledOn(thisValue: any): boolean;
/**
* Check if spy was always called with specific `this` context
* @param thisValue - The expected `this` value for all calls
* @returns Whether all calls used this context
*/
alwaysCalledOn(thisValue: any): boolean;
/**
* Check if spy was called as a constructor (with `new`)
* @returns Whether spy was called with `new` operator
*/
calledWithNew(): boolean;
/**
* Check if spy was always called as a constructor
* @returns Whether all calls used `new` operator
*/
alwaysCalledWithNew(): boolean;
}Methods for verifying spy return values and exceptions.
interface SinonSpy {
/**
* Check if spy threw an exception
* @param error - Optional specific error to check for
* @returns Whether spy threw (the specified) exception
*/
threw(error?: any): boolean;
/**
* Check if spy always threw an exception
* @param error - Optional specific error to check for
* @returns Whether spy always threw (the specified) exception
*/
alwaysThrew(error?: any): boolean;
/**
* Check if spy returned a specific value
* @param value - The expected return value
* @returns Whether spy returned this value
*/
returned(value: any): boolean;
/**
* Check if spy always returned a specific value
* @param value - The expected return value for all calls
* @returns Whether spy always returned this value
*/
alwaysReturned(value: any): boolean;
}Methods for accessing individual calls and their details.
interface SinonSpy {
/**
* Get a specific call by index
* @param index - Zero-based call index
* @returns Call object or null if index is invalid
*/
getCall(index: number): SinonSpyCall | null;
/**
* Get all calls made to the spy
* @returns Array of all call objects
*/
getCalls(): SinonSpyCall[];
/**
* Create spy subset that only considers calls with specific arguments
* @param args - Arguments to filter calls by
* @returns New spy containing only matching calls
*/
withArgs(...args: any[]): SinonSpy;
}Utility methods for managing spy state and formatting.
interface SinonSpy {
/**
* Reset the spy's call history (but keep the spy active)
*/
resetHistory(): void;
/**
* Restore the original method (for spies on object methods)
*/
restore(): void;
/**
* Format spy information with custom format string
* @param format - Format string with placeholders like %n, %c, etc.
* @param args - Additional arguments for formatting
* @returns Formatted string representation
*/
printf(format: string, ...args: any[]): string;
}Usage Examples:
const obj = { method: (x) => x * 2 };
const spy = sinon.spy(obj, "method");
obj.method(5);
obj.method(10);
// Access specific calls
console.log(spy.getCall(0).args); // [5]
console.log(spy.getCall(1).args); // [10]
// Get all calls
const calls = spy.getCalls();
console.log(calls.length); // 2
// Create filtered spy
const spy5 = spy.withArgs(5);
console.log(spy5.calledOnce); // true
// Cleanup
spy.resetHistory();
console.log(spy.callCount); // 0
spy.restore(); // Restore original methodinterface SinonSpyCall {
/** Arguments passed to this call */
args: any[];
/** The `this` value for this call */
thisValue: any;
/** Return value of this call */
returnValue: any;
/** Exception thrown by this call (if any) */
exception: Error | null;
/** Check if this call was made with specific arguments */
calledWith(...args: any[]): boolean;
/** Check if this call was made with exact arguments */
calledWithExactly(...args: any[]): boolean;
/** Check if this call was made with matching arguments */
calledWithMatch(...matchers: any[]): boolean;
/** Check if this call was made with specific `this` context */
calledOn(thisValue: any): boolean;
/** Check if this call returned a specific value */
returned(value: any): boolean;
/** Check if this call threw an exception */
threw(error?: any): boolean;
}
// Generic spy interface for typed functions
interface SinonSpy<F extends Function = Function> extends SinonSpy {
(...args: Parameters<F>): ReturnType<F>;
}Install with Tessl CLI
npx tessl i tessl/npm-sinon