Sinon.JS Documentation

repository·main·Indexed 27 days ago

https://github.com/sinonjs/sinon

A 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.

Tokens
49.2K
Snippets
101
Records
458
Agent score
92%

What's inside Sinon.JS

  1. Use the Sinon Assertions API to verify spy, stub, and mock behavior

    main
    Sinon provides a set of built-in assertion methods designed to verify the behavior of spies, stubs, and mocks. These methods allow you to assert on call counts, arguments, execution context (this), and whether the function threw an error. When an assertion fails, Sinon provides detailed error messages to help troubleshoot the test failure.
  2. Use Sinon Matchers for flexible argument matching

    main

    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:

    • Types: any, bool, date, number, string, symbol, typeOf, instanceOf.
    • Values & Logic: defined, falsy, truthy, same, match (regex/string).
    • Collections: array, object, every, some, map.
    • Property/Key Checks: has, hasOwn, hasNested, in, set.
  3. Avoid Testing Implementation Details

    main

    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));
  4. Best Practices for Using Mocks

    main

    To write maintainable and debuggable tests with Sinon mocks, follow these guidelines:

    1. One mock per test: Multiple mocks make failures difficult to diagnose.
    2. Verify once: Call verify() only once, typically in a cleanup block like afterEach.
    3. Expect only what matters: Avoid mocking every interaction; only mock what you actually need to verify.
    4. Use explicit assertions: For simpler needs, consider using sinon.fake and standard assertions instead of full mocks.
    5. Test behavior, not implementation: Avoid coupling tests to internal/private method calls. Use sinon.replace with fakes to test public behavior instead.
  5. Understand when to use Mocks

    main

    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.

    Best Practices

    • Use mocks for the method under test: In a unit test, you should ideally have only one unit under test. Mocks should be used to control how that unit is being used.
    • Declare expectations upfront: Use mocks when you want to state expectations before the action occurs, rather than asserting after the fact.
    • Avoid overusing mocks: Mocks enforce implementation details. If you wouldn't write a specific assertion for a call, do not mock it; use a stub instead.
    • Limit mock count: As a rule of thumb, a single test should contain no more than one mock (even if that mock has multiple expectations).
  6. Use stubs to prevent undesired side effects

    main

    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 pie
  7. Replace object methods using sinon.replace()

    main

    When 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'
  8. Stub ES module imports using the 'esm' package

    main
    Because ES module namespace bindings are immutable by specification, you cannot directly stub them. To allow Sinon stubs to work with ES modules, you can use the esm npm package with the mutableNamespace: true option. This allows you to configure Node.js to permit the mutation required for stubbing.
  9. Stub a dependency of a module

    main

    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");
      });
    });
  10. Create a fake with sinon.fake

    main
    Use 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.
  11. Migrate simple stub behaviors to Fakes

    main

    To migrate simple stubs, replace the sinon.stub() chain with the corresponding sinon.fake factory method:

    Stub PatternFake 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)