Profiles·Public

sinon

semver>=1.0.0postconditions22functions12last verified2026-06-23coverage score80%

Postconditions — what we check

  • stub · stub-must-restore
    error
    Whensinon.stub(obj, 'method') is called
    ThrowsTypeError: Attempted to wrap [method] which is already wrapped
    Required handlingMUST call stub.restore() in afterEach or finally block to clean up stubs. Failure to restore causes "already wrapped" errors in subsequent tests. Best practice: Use sandboxes with sandbox.restore() in afterEach. Pattern: const sandbox = sinon.createSandbox(); afterEach(() => sandbox.restore());
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • stub · stub-non-existent-property
    error
    Whensinon.stub is called on non-existent or non-function property
    ThrowsTypeError: Attempted to wrap undefined property [name]
    Required handlingMUST verify property exists and is a function before stubbing. Pattern: if (typeof obj.method === 'function') { sinon.stub(obj, 'method'); }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • stub · stub-returns-arg-invalid-index
    error
    Whenstub.returnsArg(index) is called with index >= argument count
    ThrowsTypeError: index unavailable
    Required handlingMUST ensure argument index exists before calling stub.returnsArg(index). Note: Prior to v6.1.2 returns undefined; v6.1.2+ throws TypeError.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • stub · stub-calls-arg-not-function
    error
    Whenstub.callsArg(index) is called with non-function argument
    ThrowsTypeError: index missing or not a function
    Required handlingMUST verify argument at index is a function before calling stub.callsArg(index).
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • stub · stub-yields-no-callback
    error
    Whenstub.yields() is called but stub was never called with a function argument
    ThrowsError: stub was never called with a function argument
    Required handlingMUST ensure stub is called with at least one function argument before using yields().
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • spy · spy-first-call-null-access
    error
    Whenspy.firstCall is accessed when spy was never called
    ThrowsTypeError: Cannot read property 'args' of null
    Required handlingMUST check spy.called before accessing spy.firstCall, spy.lastCall, or spy.getCall(n). Best practice: Use spy.calledWith(arg) instead of direct call access. Pattern: if (spy.called) { spy.firstCall.args }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[4]
  • spy · spy-must-restore
    error
    Whensinon.spy(obj, 'method') is called
    ThrowsTypeError: Attempted to wrap [method] which is already wrapped
    Required handlingMUST call spy.restore() in afterEach or finally block. Use sandboxes for automatic cleanup: sandbox.spy(obj, 'method'); afterEach(() => sandbox.restore());
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • mock · mock-verify-not-called
    error
    Whenmock.expects() is called but mock.verify() is never called
    ThrowsSilent failure - expectations not enforced
    Required handlingMUST call mock.verify() or sandbox.verifyAndRestore() to enforce expectations. Without verification, tests pass even when expectations are not met. Pattern: afterEach(() => { mock.verify(); }) or sandbox.verifyAndRestore()
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • mock · mock-verify-fails
    info
    Whenmock.verify() is called and expectations are not met
    ThrowsExpectationError
    Required handlingThis is expected behavior when expectations fail. Ensure expectations match actual usage.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • useFakeTimers · fake-timers-must-restore
    error
    Whensinon.useFakeTimers() is called
    ThrowsTest hangs, setTimeout/setInterval never fire in subsequent tests
    Required handlingMUST call clock.restore() in afterEach or finally block. Failure to restore causes timer pollution and test hangs. Pattern: let clock; beforeEach(() => clock = sinon.useFakeTimers()); afterEach(() => clock.restore());
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[7]
  • createSandbox · sandbox-must-restore
    error
    Whensinon.createSandbox() is called
    ThrowsTest pollution, 'already wrapped' errors in subsequent tests
    Required handlingMUST call sandbox.restore() in afterEach or finally block. Sandboxes simplify cleanup but still require explicit restoration. Pattern: const sandbox = sinon.createSandbox(); afterEach(() => sandbox.restore());
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • createStubInstance · create-stub-instance-double-use
    error
    Whensinon.createStubInstance is called twice on same constructor without cleanup
    ThrowsTypeError: Attempted to wrap [method] which is already wrapped
    Required handlingMUST restore or use different sandbox between createStubInstance calls. Pattern: Use sandbox.createStubInstance() and restore sandbox between uses.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[9]
  • fake · fake-non-function-arg-throws
    error
    Whensinon.fake(value) is called where value is not a function (e.g. a string, number, plain object, or undefined passed explicitly as first argument). sinon.fake() with zero arguments is valid; sinon.fake(someString) throws.
    ThrowsTypeError: Expected f argument to be a Function
    Required handlingMUST pass either no argument (blank fake) or a Function when calling sinon.fake(). Sub-methods (fake.returns, fake.resolves, fake.rejects, etc.) accept non-function values and are the correct API when you want a preset return value. // Correct: blank fake const f = sinon.fake(); // Correct: wrap an existing function const f2 = sinon.fake(myImpl); // Correct: preset return value via factory const f3 = sinon.fake.returns(42); // WRONG: passing a non-function const f4 = sinon.fake("not a function"); // TypeError!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10][11]
  • fake · fake-yields-missing-callback
    error
    WhenA fake created with sinon.fake.yields(...values) or sinon.fake.yieldsAsync(...values) is invoked at call time without a function as its last argument. The TypeError is thrown when the fake is called, not when it is created.
    ThrowsTypeError: Expected last argument to be a function
    Required handlingMUST ensure the last argument passed to a yields/yieldsAsync fake is a function. This is the callback convention: the fake expects to invoke its last argument. // Correct: caller always passes a callback last const fake = sinon.fake.yields("result"); fake("arg1", (result) => console.log(result)); // ok // WRONG: no callback fake("arg1"); // TypeError at invocation time!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10][11]
  • replace · replace-non-existent-property
    error
    Whensandbox.replace(object, property, replacement) is called where `property` does not exist on `object` or its prototype chain. A common mistake when refactoring code that removes or renames properties while tests still reference old names.
    ThrowsTypeError: Cannot replace non-existent property '<property>'. Perhaps you meant sandbox.define()?
    Required handlingMUST verify the property exists on the object before calling sandbox.replace(). If the goal is to add a new property (not replace an existing one), use sandbox.define(object, property, value) instead. // Correct: property exists const obj = { method: () => 'original' }; sandbox.replace(obj, 'method', sandbox.stub()); // ok // WRONG: property doesn't exist sandbox.replace(obj, 'nonExistent', sandbox.stub()); // TypeError!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][12]
  • replace · replace-already-replaced-throws
    error
    Whensandbox.replace(object, property, replacement) is called on a property that has already been replaced via sandbox.replace() in the same sandbox without an intervening sandbox.restore(). Happens when the same setup code is called twice, or two beforeEach hooks both replace the same property.
    ThrowsTypeError: Attempted to replace '<property>' which is already replaced
    Required handlingMUST call sandbox.restore() between replace calls on the same property. Use a single sandbox per test scope and restore in afterEach. // Correct: one replace per sandbox lifecycle beforeEach(() => { sandbox.replace(obj, 'method', replacement); }); afterEach(() => { sandbox.restore(); // clears the replacement }); // WRONG: replacing twice without restore sandbox.replace(obj, 'method', stub1); // ok sandbox.replace(obj, 'method', stub2); // TypeError: already replaced!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][12]
  • replace · replace-accessor-property-throws
    error
    Whensandbox.replace(object, property, replacement) is called on a property that is defined as a getter or setter (accessor property). sandbox.replace() only works on value properties (data descriptors). Attempting to replace a getter or setter throws an Error directing to use replaceGetter/replaceSetter.
    ThrowsError: Use sandbox.replaceGetter for replacing getters
    Required handlingUse sandbox.replaceGetter() for getter properties, sandbox.replaceSetter() for setter properties, or sandbox.replace.usingAccessor() when you need to replace the underlying value of an accessor property. // Correct: use replaceGetter for getters sandbox.replaceGetter(obj, 'computedProp', () => 'test-value'); // WRONG: using replace() on a getter // if 'computedProp' is a getter: sandbox.replace(obj, 'computedProp', 'test-value'); // Error!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][12]
  • replace · replace-type-mismatch-throws
    error
    Whensandbox.replace(object, property, replacement) is called where the replacement is a different JavaScript type than the original property value. For example, replacing a function property with a plain object, or replacing a string property with a number. sinon enforces type consistency to prevent accidental misuse.
    ThrowsTypeError: Cannot replace <originalType> with <replacementType>
    Required handlingMUST pass a replacement of the same type as the original property. When replacing a function, use a stub/spy/fake (which are functions). When replacing a primitive, use the same primitive type. const obj = { value: 'original-string' }; // Correct: same type sandbox.replace(obj, 'value', 'replacement-string'); // ok // WRONG: different type sandbox.replace(obj, 'value', 42); // TypeError: Cannot replace string with number!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][12]
  • define · define-existing-property-throws
    error
    Whensandbox.define(object, property, value) is called where `property` already exists as an own property on `object`. Common mistake when test setup code reuses the same property name across tests, or when refactoring stub() → define() without checking whether the property pre-exists. Also fires when `property` is undefined (e.g. sandbox.define(obj, obj.missingKey, val) where missingKey resolves to undefined).
    ThrowsTypeError: Cannot define the already existing property '<property>'. Perhaps you meant sandbox.replace()?
    Required handlingMUST use sandbox.replace() (or sandbox.stub() for functions) when the property already exists on the object. Use sandbox.define() only to add NEW properties that need to be tracked for cleanup. const obj = { existingMethod: () => 'original' }; // Correct: define a NEW property (does not exist yet) sandbox.define(obj, 'newMethod', sandbox.stub().returns('test')); // Correct: replace an EXISTING property sandbox.replace(obj, 'existingMethod', sandbox.stub().returns('test')); // WRONG: define on an existing property sandbox.define(obj, 'existingMethod', sandbox.stub()); // TypeError!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][13]
  • verify · verify-unmet-expectation-throws
    error
    Whensandbox.verify() is called and at least one registered mock has an unmet expectation (e.g. expects(method).withArgs(X) was set but the method was never called with X, or was called fewer/more times than expects(method).once()/atLeast()/exactly()). This is the canonical 'mock verification failed' signal — typical in test assertions but DANGEROUS in production code or test cleanup hooks that swallow the error and let subsequent tests pollute the state.
    ThrowsExpectationError: expected <method> to be called with arguments matching ... but was called <n> times
    Required handlingWhen sandbox.verify() is called outside a test framework's explicit assertion phase (e.g. in afterEach where the test result has already been reported), the caller MUST wrap in try/catch to attach the verification failure to the right test, AND MUST still call sandbox.restore() in a finally block to clean up the sandbox state. Otherwise the ExpectationError escapes into the test framework, subsequent tests run against a polluted sandbox, and the original failing test may already be marked passing. Best practice: use sandbox.verifyAndRestore() instead — it wraps the verify+ restore pattern correctly: // Correct: verifyAndRestore handles try/finally for you afterEach(() => { sandbox.verifyAndRestore(); }); // Manual verify path requires explicit try/finally afterEach(() => { try { sandbox.verify(); } finally { sandbox.restore(); } }); // WRONG: bare verify() — if verification fails, restore() is skipped afterEach(() => { sandbox.verify(); sandbox.restore(); // never reached if verify throws! });
    costmediumin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[8][6][13]
  • verifyAndRestore · verify-and-restore-expectation-throws
    error
    Whensandbox.verifyAndRestore() is called and at least one registered mock has an unmet expectation. Unlike bare verify(), the sandbox IS guaranteed restored before ExpectationError propagates — but the test framework still receives the throw, so the caller must let it bubble to a test failure or catch it.
    ThrowsExpectationError: expected <method> to be called with arguments matching ... but was called <n> times
    Required handlingIn test afterEach hooks, allow the ExpectationError to propagate so the test framework can record the failure. The sandbox is guaranteed restored, so no finally block is required. Production code calling verifyAndRestore() (e.g. mock-driven integration tests run in CI) MUST allow the throw to surface or wrap in try/catch to record the assertion failure; swallowing it silently means failing mocks pass the test suite undetected. // Correct: afterEach lets the error reach the test framework afterEach(() => { sandbox.verifyAndRestore(); }); // Correct: explicit catch records the failure try { sandbox.verifyAndRestore(); } catch (e) { reporter.recordFailure(e); throw e; } // WRONG: silent swallow — mock failures pass undetected try { sandbox.verifyAndRestore(); } catch {}
    costmediumin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[8][13]
  • restoreObject · restore-object-falsy-input-throws
    error
    Whensinon.restoreObject(object) is called with a falsy value: null, undefined, 0, false, NaN, or ''. Common in test teardown code that loops over a collection of stubbed targets and forgets to filter out already-cleared references, or where a teardown helper receives an optional object that is sometimes absent.
    ThrowsError: Trying to restore object but received <stringified-falsy-value>
    Required handlingMUST guard with a truthy check before calling sinon.restoreObject(), or wrap the call in try/catch in cleanup loops that may legitimately encounter already-cleared references. // Correct: guard if (target) { sinon.restoreObject(target); } // Correct: defensive try-catch in a cleanup loop for (const target of targets) { try { sinon.restoreObject(target); } catch { /* already cleared */ } } // WRONG: no guard sinon.restoreObject(maybeUndefined); // throws Error!
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[14][15]

Sources

Every postcondition cites at least one of these. Numbered to match the footnotes above.

  1. [1]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/issues/1673
  2. [2]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/issues/1762
  3. [3]sinonjs.org/releases/latesthttps://sinonjs.org/releases/latest/stubs/
  4. [4]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/issues/1936
  5. [5]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/issues/1775
  6. [6]sinonjs.org/releases/latesthttps://sinonjs.org/releases/latest/mocks/
  7. [7]sinonjs.org/releases/latesthttps://sinonjs.org/releases/latest/fake-timers/
  8. [8]sinonjs.org/releases/latesthttps://sinonjs.org/releases/latest/sandbox/
  9. [9]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/issues/852
  10. [10]sinonjs.org/releases/latesthttps://sinonjs.org/releases/latest/fakes/
  11. [11]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/blob/main/lib/sinon/fake.js
  12. [12]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/blob/main/lib/sinon/sandbox.js
  13. [13]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/blob/v22.0.0/src/sinon/sandbox.js
  14. [14]sinonjs.org/releases/latesthttps://sinonjs.org/releases/latest/utils/
  15. [15]github.com/sinonjs/sinonhttps://github.com/sinonjs/sinon/blob/v22.0.0/src/sinon/restore-object.js
Need a different package?
Request a profile