Sinon.JS Test Spies, Stubs, and Mocks
Sinon.js is a widely used, standalone, and test framework-agnostic JavaScript library providing test spies, stubs, and mocks for robust unit testing. The current stable version is 21.1.2. It maintains a consistent release cadence, frequently publishing updates and bug fixes across major versions. Key differentiators include its non-global pollution approach, ease of integration with any testing framework (like Mocha, Jest, or QUnit), and built-in fakes for browser APIs such as timers (setTimeout, setInterval) and XMLHttpRequest. It is designed to be easy to use and requires minimal setup, allowing developers to isolate and test specific units of code effectively by controlling their dependencies and behavior.
Common errors
-
TypeError: sinon.stub is not a function
cause This error typically occurs when attempting to destructure `stub` as a named import (e.g., `import { stub } from 'sinon';`) or when `sinon` itself is not correctly imported.fixEnsure Sinon is imported as a default export: `import sinon from 'sinon';` (ESM) or `const sinon = require('sinon');` (CommonJS), and then access `stub` as a property: `sinon.stub()`. -
TypeError: Cannot read properties of undefined (reading 'reset') OR console warning: 'spy.reset()' is deprecated. Use 'spy.resetHistory()' instead.
cause Attempting to use the `spy.reset()` method, which has been deprecated since v4.1.4 and is no longer the recommended way to clear a spy's state.fixReplace all calls to `spy.reset()` with `spy.resetHistory()` to clear the call history, arguments, and return values of a spy or stub. Use `spy.restore()` to revert the original method. -
Error: Cannot stub non-existent property 'someMethodName'
cause Sinon cannot stub a method or property that does not exist on the target object at the moment `sinon.stub()` is called.fixVerify that the method or property you intend to stub (`someMethodName`) exists on the object you are passing to `sinon.stub()`. This often happens with dynamic properties or when stubbing properties on prototypes that haven't been correctly inherited or defined.
Warnings
- breaking Sinon v3.0.0 removed several previously deprecated exports. Additionally, `fakeXhr`, `fakeServer`, and `fakeServerWithClock` functionalities were extracted into the `nise` package, though they were re-imported into Sinon's top-level API. Direct access to these modules via their old, internal paths may break.
- deprecated The `spy.reset()` method was deprecated in favor of `spy.resetHistory()` starting from v4.1.4. While `reset()` still works in current versions (21.x.x), it emits a deprecation warning and may be removed in future major releases.
- gotcha Version 4.1.5 contained a bug where `sinon.useFakeServer()` could return an unexpected server type, leading to incorrect test behavior. This issue was promptly addressed in the following patch release.
- gotcha Failure to call `clock.restore()` after `sinon.useFakeTimers()` can lead to global state pollution, causing unexpected behavior in subsequent tests or other parts of the application that rely on real timer functions or the global `Date` object.
Install
-
npm install sinon -
yarn add sinon -
pnpm add sinon
Imports
- sinon
const sinon = require('sinon');import sinon from 'sinon';
- stub
import { stub } from 'sinon';import sinon from 'sinon'; sinon.stub(obj, 'method');
- useFakeTimers
import sinon from 'sinon'; const clock = sinon.useFakeTimers();
Quickstart
import sinon from 'sinon';
// Simulate an external API dependency
class ExternalAPI {
async getData(): Promise<string> {
// In a real application, this would make an actual network call
return Promise.resolve('Real Data From Server');
}
}
// Our code under test that depends on ExternalAPI
class MyService {
constructor(private api: ExternalAPI) {}
async processData(): Promise<string> {
const data = await this.api.getData();
return data.toUpperCase();
}
}
async function runExample() {
const api = new ExternalAPI();
const service = new MyService(api);
// Create a stub for the API's getData method
const getDataStub = sinon.stub(api, 'getData');
// Configure the stub to return a predictable, mock value
getDataStub.resolves('Mocked Data');
// Call the method under test, which now uses the stubbed getData
const result = await service.processData();
console.log('Processed Result:', result); // Expected output: MOCKED DATA
console.log('getData was called once:', getDataStub.calledOnce); // Expected output: true
// Restore the original method to clean up after the test
getDataStub.restore();
}
runExample().catch(console.error);