Use the Sinon Assertions API to verify spy, stub, and mock behavior
mainthis), and whether the function threw an error. When an assertion fails, Sinon provides detailed error messages to help troubleshoot the test failure.repository·main·Indexed 27 days ago
https://github.com/sinonjs/sinonA standalone JavaScript testing utility providing spies, stubs, and mocks. Designed to work with any testing framework, Sinon.JS allows for flexible assertions via sinon.match, time manipulation with fake-timers, and isolated test environments using sandboxes. Recent versions include support for ECMAScript Modules (ESM), the Temporal API, and Node.js 26.
this), and whether the function threw an error. When an assertion fails, Sinon provides detailed error messages to help troubleshoot the test failure.Sinon provides a wide range of matchers to allow for flexible argument matching in assertions and stubs. Instead of matching exact values, you can use matchers to verify types, specific properties, or custom logic.
Available matcher categories include:
any, bool, date, number, string, symbol, typeOf, instanceOf.defined, falsy, truthy, same, match (regex/string).array, object, every, some, map.has, hasOwn, hasNested, in, set.A common anti-pattern is using mocks to verify how a piece of code works (e.g., specific SQL queries). Instead, use fakes to verify what the code does (the resulting behavior). This makes tests resilient to internal refactoring.
// Good: Testing behavior (WHAT the code does)
const fake = sinon.fake.resolves({ user: userData, posts: postsData });
sinon.replace(database, "getUserWithPosts", fake);
const result = await controller.getUserWithPosts(userId);
assert.deepEqual(result.user, expectedUser);
assert.deepEqual(result.posts, expectedPosts);
assert.ok(fake.calledWith(userId));To write maintainable and debuggable tests with Sinon mocks, follow these guidelines:
verify() only once, typically in a cleanup block like afterEach.sinon.fake and standard assertions instead of full mocks.sinon.replace with fakes to test public behavior instead.Mocks are fake methods that combine the capabilities of spies (tracking calls) and stubs (pre-programmed behavior) with pre-programmed expectations. A mock will automatically fail your test if it is not used exactly as expected.
Use a stub to prevent a specific method from being called directly, which is useful for methods that trigger undesired side effects like fs.readFile. You can use .callsFake() to provide a controlled implementation (e.g., returning a resolved Promise) instead of the real implementation.
import * as sinon from "sinon";
import * as fs from "fs";
// stub out the readFile method
sinon.stub(fs, "readFile").callsFake(function () {
// and make it return the value we want for our test
return Promise.resolve("Apple pie");
});
const fileContent = await fs.readFile("somefile");
console.log(fileContent);
// => Apple pieWhen using stubs to replace an existing object method, sinon.stub(obj, 'method') combines creation and replacement. When using fakes, you must separate these steps: first create the fake, then use sinon.replace() to inject it into the object.
const obj = {
method() {
return "original";
}
};
// Create the fake separately
const fake = sinon.fake.returns("stubbed");
// Use sinon.replace to plug it in
sinon.replace(obj, "method", fake);
obj.method(); // 'stubbed'esm npm package with the mutableNamespace: true option. This allows you to configure Node.js to permit the mutation required for stubbing.sinon.stub(object, 'method', func) signature has been removed. Use the .callsFake() method instead to ensure you get a full stub that allows behavior redefinition.Sinon is a stubbing library, not a module interception library. To stub an imported module dependency, you must explicitly import the dependency in your test file and use sinon.stub() on the desired method of that imported object.
Critical Requirement: For stubbing to work, the method being stubbed cannot be destructured in either the module under test or in the test file. The module under test must access the dependency via its object reference (e.g., dependencyModule.method()) rather than extracting the function directly (e.g., const { method } = require('./dependencyModule')).
const assert = require("assert");
const sinon = require("sinon");
// 1. Import the dependency explicitly in the test
const dependencyModule = require("./dependencyModule");
// 2. Import the module under test
const { getTheSecret } = require("./moduleUnderTest");
describe("moduleUnderTest", function () {
it("should return a stubbed value", function () {
// 3. Stub the method on the dependency object
sinon.stub(dependencyModule, "getSecretNumber").returns(3);
const result = getTheSecret();
assert.equal(result, "The secret was: 3");
});
});sinon.fake to create a simple, immutable test double. A fake is a function that records arguments, return values, the value of this, and any errors thrown for every call. Unlike spies or stubs, a fake's behavior is immutable once created, meaning you set its behavior at instantiation rather than changing it later.To migrate simple stubs, replace the sinon.stub() chain with the corresponding sinon.fake factory method:
| Stub Pattern | Fake Pattern |
|---|---|
.returns(val) | sinon.fake.returns(val) |
.throws(err) | sinon.fake.throws(err) |
.resolves(val) | sinon.fake.resolves(val) |
.rejects(err) | sinon.fake.rejects(err) |
.yields(arg1, ...) | sinon.fake.yields(arg1, ...) |
.yieldsAsync(arg1, ...) | sinon.fake.yieldsAsync(arg1, ...) |
.callsFake(fn) | sinon.fake(fn) |