JavaScript test spies, stubs and mocks for framework-agnostic unit testing.
npx @tessl/cli install tessl/npm-sinon@21.0.0Sinon.js is a comprehensive JavaScript testing library providing standalone test spies, stubs, and mocks. It offers framework-agnostic test doubles for creating robust unit tests, enabling developers to intercept function calls, control return values, and verify interactions without modifying the original codebase.
npm install sinonimport sinon from "sinon";For CommonJS:
const sinon = require("sinon");Individual imports:
import { spy, stub, mock, fake, assert, match, createSandbox, createStubInstance } from "sinon";import sinon from "sinon";
// Create a spy to track calls
const callback = sinon.spy();
callback("hello", "world");
console.log(callback.calledWith("hello", "world")); // true
// Create a stub with behavior
const fetchData = sinon.stub().resolves({ data: "test" });
const result = await fetchData();
console.log(result); // { data: "test" }
// Mock an object method
const user = { getName: () => "original" };
const mock = sinon.mock(user);
mock.expects("getName").once().returns("mocked");
console.log(user.getName()); // "mocked"
mock.verify(); // Passes - expectation met
mock.restore();Sinon.js is built around several key components:
Isolated testing environment that manages all fakes and provides automatic cleanup. Essential for test isolation and preventing test interference.
function createSandbox(options?: SandboxOptions): SinonSandbox;
interface SinonSandbox {
spy(): SinonSpy;
spy<F extends Function>(func: F): SinonSpy<F>;
spy<T>(object: T, method: keyof T): SinonSpy;
stub(): SinonStub;
stub<T>(object: T, method: keyof T): SinonStub;
mock(object: any): SinonMock;
fake(func?: Function): SinonFake;
restore(): void;
reset(): void;
resetBehavior(): void;
resetHistory(): void;
}
interface SandboxOptions {
injectInto?: object;
properties?: string[];
useFakeTimers?: boolean | FakeTimerConfig;
useFakeServer?: boolean;
}Record function calls, arguments, return values, and exceptions without changing the original function's behavior. Perfect for verifying function interactions.
function spy(): SinonSpy;
function spy<F extends Function>(func: F): SinonSpy<F>;
function spy<T>(object: T, method: keyof T): SinonSpy;
interface SinonSpy {
called: boolean;
callCount: number;
calledOnce: boolean;
calledTwice: boolean;
calledThrice: boolean;
calledWith(...args: any[]): boolean;
calledWithExactly(...args: any[]): boolean;
alwaysCalledWith(...args: any[]): boolean;
firstCall: SinonSpyCall;
lastCall: SinonSpyCall;
getCall(index: number): SinonSpyCall;
getCalls(): SinonSpyCall[];
}Spies with programmable behavior for controlling return values, exceptions, and callback execution. Ideal for isolating code under test from dependencies.
function stub(): SinonStub;
function stub<T>(object: T, method: keyof T): SinonStub;
interface SinonStub extends SinonSpy {
returns(value: any): SinonStub;
throws(error?: any): SinonStub;
resolves(value?: any): SinonStub;
rejects(error?: any): SinonStub;
callsFake(func: Function): SinonStub;
callsThrough(): SinonStub;
yields(...args: any[]): SinonStub;
callsArg(index: number): SinonStub;
withArgs(...args: any[]): SinonStub;
onCall(index: number): SinonStub;
onFirstCall(): SinonStub;
}Pre-programmed expectations for verifying specific method calls and their parameters. Excellent for testing precise interaction contracts.
function mock(object: any): SinonMock;
interface SinonMock {
expects(method: string): SinonExpectation;
restore(): void;
verify(): void;
}
interface SinonExpectation {
once(): SinonExpectation;
twice(): SinonExpectation;
thrice(): SinonExpectation;
exactly(count: number): SinonExpectation;
atLeast(count: number): SinonExpectation;
atMost(count: number): SinonExpectation;
withArgs(...args: any[]): SinonExpectation;
returns(value: any): SinonExpectation;
throws(error?: any): SinonExpectation;
}Lightweight alternative to spies providing call recording with optional custom implementations. Simpler than stubs for basic testing scenarios.
function fake(func?: Function): SinonFake;
// Static creation methods
declare namespace fake {
function returns(value: any): SinonFake;
function throws(error?: any): SinonFake;
function resolves(value?: any): SinonFake;
function rejects(error?: any): SinonFake;
function yields(...args: any[]): SinonFake;
}
interface SinonFake extends SinonSpy {
// Inherits all spy properties and methods
}Control time-dependent code by mocking JavaScript's timing functions (setTimeout, setInterval) and Date constructor. Essential for testing time-based logic.
function useFakeTimers(config?: FakeTimerConfig): SinonClock;
interface SinonClock {
tick(milliseconds: number): void;
next(): void;
runAll(): void;
runToLast(): void;
restore(): void;
Date: DateConstructor;
setTimeout: typeof setTimeout;
clearTimeout: typeof clearTimeout;
setInterval: typeof setInterval;
clearInterval: typeof clearInterval;
}
interface FakeTimerConfig {
now?: number | Date;
toFake?: string[];
shouldAdvanceTime?: boolean;
advanceTimeDelta?: number;
}Comprehensive assertion methods for testing spy, stub, and mock behavior. Provides clear error messages for failed expectations.
declare namespace assert {
function called(spy: SinonSpy): void;
function notCalled(spy: SinonSpy): void;
function calledOnce(spy: SinonSpy): void;
function calledTwice(spy: SinonSpy): void;
function calledThrice(spy: SinonSpy): void;
function calledWith(spy: SinonSpy, ...args: any[]): void;
function calledWithExactly(spy: SinonSpy, ...args: any[]): void;
function alwaysCalledWith(spy: SinonSpy, ...args: any[]): void;
function threw(spy: SinonSpy, error?: any): void;
function callOrder(...spies: SinonSpy[]): void;
}Flexible argument matching system for creating sophisticated test assertions and stub behaviors. Supports type-based, property-based, and custom matchers.
declare namespace match {
// Type matchers
const any: SinonMatcher;
const bool: SinonMatcher;
const number: SinonMatcher;
const string: SinonMatcher;
const object: SinonMatcher;
const func: SinonMatcher;
const array: SinonMatcher;
// Value matchers
function same(value: any): SinonMatcher;
function typeOf(type: string): SinonMatcher;
function instanceOf(constructor: Function): SinonMatcher;
// Property matchers
function has(property: string, value?: any): SinonMatcher;
function hasOwn(property: string, value?: any): SinonMatcher;
// Custom matchers
function (predicate: (value: any) => boolean, message?: string): SinonMatcher;
}
interface SinonMatcher {
and(matcher: SinonMatcher): SinonMatcher;
or(matcher: SinonMatcher): SinonMatcher;
}Create stubbed instances of constructors with all methods replaced by stubs. Essential for testing code that depends on class instances without invoking the real constructor.
function createStubInstance<T>(
constructor: SinonStubCreateStubInstanceConstructor<T>,
overrides?: SinonStubbedInstance<T>
): SinonStubbedInstance<T>;
type SinonStubbedInstance<T> = T & {
[K in keyof T]: T[K] extends Function ? SinonStub : T[K];
};Controllable Promise implementation for testing asynchronous code without relying on timing or external Promise resolution.
function promise<T = any>(executor?: PromiseExecutor<T>): SinonPromise<T>;
interface SinonPromise<T> extends Promise<T> {
status: "pending" | "resolved" | "rejected";
resolvedValue?: T;
rejectedValue?: any;
resolve(value?: T): SinonPromise<T>;
reject(reason?: any): Promise<void>;
}Extend stub functionality by adding custom behavior methods. Allows creating reusable stub patterns and domain-specific testing utilities.
function addBehavior(name: string, fn: Function): void;Usage Examples:
import sinon from "sinon";
// Add a custom behavior for returning promises
sinon.addBehavior("returnsPromise", function(fake, value) {
fake.returns(Promise.resolve(value));
});
// Add a custom behavior for delayed responses
sinon.addBehavior("returnsAfterDelay", function(fake, value, delay) {
fake.returns(new Promise(resolve => {
setTimeout(() => resolve(value), delay);
}));
});
// Add a behavior for sequential returns
sinon.addBehavior("returnsSequentially", function(fake, ...values) {
values.forEach((value, index) => {
fake.onCall(index).returns(value);
});
});
// Use the custom behaviors
const apiStub = sinon.stub();
apiStub.returnsPromise({ data: "test" });
const delayedStub = sinon.stub();
delayedStub.returnsAfterDelay("delayed result", 100);
const sequentialStub = sinon.stub();
sequentialStub.returnsSequentially("first", "second", "third");
// Test the behaviors
console.log(await apiStub()); // { data: "test" }
console.log(sequentialStub()); // "first"
console.log(sequentialStub()); // "second"
console.log(sequentialStub()); // "third"interface SinonSpyCall {
args: any[];
thisValue: any;
returnValue: any;
exception: Error;
calledWith(...args: any[]): boolean;
calledWithExactly(...args: any[]): boolean;
returned(value: any): boolean;
threw(error?: any): boolean;
}
// Global sinon object extends SinonSandbox
interface SinonStatic extends SinonSandbox {
createSandbox(options?: SandboxOptions): SinonSandbox;
createStubInstance<T>(
constructor: SinonStubCreateStubInstanceConstructor<T>,
overrides?: SinonStubbedInstance<T>
): SinonStubbedInstance<T>;
match: typeof match;
assert: typeof assert;
restoreObject(object: any): void;
// Additional top-level exports
expectation: SinonExpectation;
timers: any; // From @sinonjs/fake-timers
addBehavior(name: string, fn: Function): void;
promise<T = any>(executor?: PromiseExecutor<T>): SinonPromise<T>;
}