BUPKIS
    Preparing search index...

    @bupkis/sinon

    Sinon spy/stub/mock assertions for Bupkis.

    npm install @bupkis/sinon bupkis sinon
    
    import { use } from 'bupkis';
    import sinonAssertions from '@bupkis/sinon';
    import sinon from 'sinon';

    const { expect } = use(sinonAssertions);

    // Basic spy assertions
    const spy = sinon.spy();
    spy(42);
    expect(spy, 'was called');
    expect(spy, 'was called once');
    expect(spy, 'was called with', [42]);

    // Call count
    spy();
    spy();
    expect(spy, 'was called times', 3);

    // Stub return values
    const stub = sinon.stub().returns(100);
    stub();
    expect(stub.firstCall, 'to have returned', 100);

    // Call order
    const first = sinon.spy();
    const second = sinon.spy();
    first();
    second();
    expect(first, 'was called before', second);
    expect([first, second], 'given call order');

    // Complex call specifications
    const logger = sinon.spy();
    logger('info', 'started');
    logger('debug', 'processing');
    logger('info', 'done');
    expect(logger, 'to have calls satisfying', [
    ['info', 'started'],
    ['debug', 'processing'],
    ['info', 'done'],
    ]);

    ✏️ Aliases:

    {Spy} was called
    {Spy} to have been called
    

    Asserts that a spy was called at least once.

    Success:

    const spy = sinon.spy();
    spy();
    expect(spy, 'was called');
    expect(spy, 'to have been called');

    Failure:

    const spy = sinon.spy();
    expect(spy, 'was called');
    // AssertionError: Expected spy to have been called, but it was never called

    Negation:

    const spy = sinon.spy();
    expect(spy, 'not to have been called');

    ✏️ Aliases:

    {Spy} was not called
    {Spy} to not have been called
    

    Asserts that a spy was never called.

    Success:

    const spy = sinon.spy();
    expect(spy, 'was not called');
    expect(spy, 'to not have been called');

    Failure:

    const spy = sinon.spy();
    spy();
    expect(spy, 'was not called');
    // AssertionError: Expected spy to not have been called, but it was called 1 time(s)

    Negation:

    const spy = sinon.spy();
    spy();
    expect(spy, 'not was not called'); // awkward but valid

    ✏️ Aliases:

    {Spy} was called once
    {Spy} to have been called once
    

    Asserts that a spy was called exactly once.

    Success:

    const spy = sinon.spy();
    spy();
    expect(spy, 'was called once');
    expect(spy, 'to have been called once');

    Failure:

    const spy = sinon.spy();
    spy();
    spy();
    expect(spy, 'was called once');
    // AssertionError: Expected spy to have been called exactly once

    Negation:

    const spy = sinon.spy();
    expect(spy, 'not to have been called once');

    Asserts that a spy was called exactly twice.

    Success:

    const spy = sinon.spy();
    spy();
    spy();
    expect(spy, 'was called twice');

    Failure:

    const spy = sinon.spy();
    spy();
    expect(spy, 'was called twice');
    // AssertionError: Expected spy to have been called exactly twice

    Negation:

    const spy = sinon.spy();
    expect(spy, 'not was called twice');

    Asserts that a spy was called exactly three times.

    Success:

    const spy = sinon.spy();
    spy();
    spy();
    spy();
    expect(spy, 'was called thrice');

    Failure:

    const spy = sinon.spy();
    spy();
    spy();
    expect(spy, 'was called thrice');
    // AssertionError: Expected spy to have been called exactly three times

    Negation:

    const spy = sinon.spy();
    expect(spy, 'not was called thrice');

    Asserts that a spy was called exactly the specified number of times.

    Success:

    const spy = sinon.spy();
    spy();
    spy();
    spy();
    spy();
    spy();
    expect(spy, 'was called times', 5);

    Failure:

    const spy = sinon.spy();
    spy();
    spy();
    expect(spy, 'was called times', 5);
    // AssertionError: Expected spy to have been called 5 time(s)

    Negation:

    const spy = sinon.spy();
    spy();
    expect(spy, 'not was called times', 5);

    ✏️ Aliases:

    {Spy} to have returned
    {Spy} returned
    

    Asserts that a spy returned successfully (without throwing) at least once.

    Success:

    const spy = sinon.spy(() => 42);
    spy();
    expect(spy, 'to have returned');
    expect(spy, 'returned');

    Failure:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    expect(spy, 'to have returned');
    // AssertionError: Expected spy to have returned at least once

    Negation:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    expect(spy, 'not to have returned');

    Asserts that a spy returned successfully exactly the specified number of times.

    Success:

    const spy = sinon.spy(() => 42);
    spy();
    spy();
    spy();
    expect(spy, 'to have returned times', 3);

    Failure:

    const spy = sinon.spy(() => 42);
    spy();
    spy();
    expect(spy, 'to have returned times', 3);
    // AssertionError: Expected spy to have returned 3 time(s), but it returned 2 time(s)

    Negation:

    const spy = sinon.spy(() => 42);
    spy();
    expect(spy, 'not to have returned times', 3);

    Asserts that a spy returned the specified value on at least one call. Uses Sinon's spy.returned() method for comparison (deep equality via samsam).

    Success:

    const spy = sinon.spy((x) => x * 2);
    spy(5);
    spy(10);
    expect(spy, 'to have returned with', 10);
    expect(spy, 'to have returned with', 20);

    Failure:

    const spy = sinon.spy((x) => x * 2);
    spy(5);
    expect(spy, 'to have returned with', 100);
    // AssertionError: Expected spy to have returned specified value

    Negation:

    const spy = sinon.spy((x) => x * 2);
    spy(5);
    expect(spy, 'not to have returned with', 100);

    ✏️ Aliases:

    {Spy} was called with {array}
    {Spy} to have been called with {array}
    

    Asserts that at least one call to the spy included the specified arguments. Uses prefix matching: the spy may have been called with additional arguments beyond those specified.

    Success:

    const spy = sinon.spy();
    spy('foo', 42, 'extra');
    expect(spy, 'was called with', ['foo', 42]); // prefix match - 'extra' ignored
    expect(spy, 'to have been called with', ['foo', 42, 'extra']); // exact match

    Failure:

    const spy = sinon.spy();
    spy('bar');
    expect(spy, 'was called with', ['foo']);
    // AssertionError: Expected spy to have been called with specified arguments

    Negation:

    const spy = sinon.spy();
    spy('bar');
    expect(spy, 'not to have been called with', ['foo']);

    Asserts that all calls to the spy included the specified arguments (prefix match).

    Success:

    const spy = sinon.spy();
    spy('foo', 1);
    spy('foo', 2);
    spy('foo', 3);
    expect(spy, 'was always called with', ['foo']);

    Failure:

    const spy = sinon.spy();
    spy('foo');
    spy('bar');
    expect(spy, 'was always called with', ['foo']);
    // AssertionError: Expected spy to always have been called with specified arguments

    Negation:

    const spy = sinon.spy();
    spy('foo');
    spy('bar');
    expect(spy, 'not was always called with', ['foo']);

    Asserts that at least one call to the spy had exactly the specified arguments (no additional arguments).

    Success:

    const spy = sinon.spy();
    spy('foo', 42);
    expect(spy, 'was called with exactly', ['foo', 42]);

    Failure:

    const spy = sinon.spy();
    spy('foo', 42, 'extra');
    expect(spy, 'was called with exactly', ['foo', 42]);
    // AssertionError: Expected spy to have been called with exactly the specified arguments

    Negation:

    const spy = sinon.spy();
    spy('foo', 42, 'extra');
    expect(spy, 'not was called with exactly', ['foo', 42]);

    Asserts that the spy was never called with the specified arguments.

    Success:

    const spy = sinon.spy();
    spy('foo');
    spy('bar');
    expect(spy, 'was never called with', ['baz']);

    Failure:

    const spy = sinon.spy();
    spy('foo');
    expect(spy, 'was never called with', ['foo']);
    // AssertionError: Expected spy to never have been called with specified arguments

    Negation:

    const spy = sinon.spy();
    spy('foo');
    expect(spy, 'not was never called with', ['foo']);

    ✏️ Aliases:

    {Spy} was called on {unknown}
    {Spy} to have been called on {unknown}
    

    Asserts that at least one call to the spy used the specified this context.

    Success:

    const obj = { name: 'test' };
    const spy = sinon.spy();
    spy.call(obj);
    expect(spy, 'was called on', obj);
    expect(spy, 'to have been called on', obj);

    Failure:

    const obj1 = { name: 'one' };
    const obj2 = { name: 'two' };
    const spy = sinon.spy();
    spy.call(obj1);
    expect(spy, 'was called on', obj2);
    // AssertionError: Expected spy to have been called with specified this context

    Negation:

    const obj1 = { name: 'one' };
    const obj2 = { name: 'two' };
    const spy = sinon.spy();
    spy.call(obj1);
    expect(spy, 'not to have been called on', obj2);

    Asserts that all calls to the spy used the specified this context.

    Success:

    const obj = { name: 'test' };
    const spy = sinon.spy();
    spy.call(obj);
    spy.call(obj);
    expect(spy, 'was always called on', obj);

    Failure:

    const obj1 = { name: 'one' };
    const obj2 = { name: 'two' };
    const spy = sinon.spy();
    spy.call(obj1);
    spy.call(obj2);
    expect(spy, 'was always called on', obj1);
    // AssertionError: Expected spy to always have been called with specified this context

    Negation:

    const obj1 = { name: 'one' };
    const obj2 = { name: 'two' };
    const spy = sinon.spy();
    spy.call(obj1);
    spy.call(obj2);
    expect(spy, 'not was always called on', obj1);

    ✏️ Aliases:

    {Spy} threw
    {Spy} to have thrown
    

    Asserts that the spy threw an exception on at least one call.

    Success:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    expect(spy, 'threw');
    expect(spy, 'to have thrown');

    Failure:

    const spy = sinon.spy();
    spy();
    expect(spy, 'threw');
    // AssertionError: Expected spy to have thrown an exception

    Negation:

    const spy = sinon.spy();
    spy();
    expect(spy, 'not to have thrown');

    Asserts that the spy threw a specific error. The parameter can be an Error instance or a string representing the error type name.

    Success:

    const spy = sinon.spy(() => {
    throw new TypeError('bad type');
    });
    try {
    spy();
    } catch {}
    expect(spy, 'threw', 'TypeError'); // match by type name
    expect(spy, 'threw', new TypeError('bad type')); // match by instance

    Failure:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    expect(spy, 'threw', 'TypeError');
    // AssertionError: Expected spy to have thrown specified exception

    Negation:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    expect(spy, 'not threw', 'TypeError');

    Asserts that the spy threw an exception on every call.

    Success:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    try {
    spy();
    } catch {}
    expect(spy, 'always threw');

    Failure:

    let shouldThrow = true;
    const spy = sinon.spy(() => {
    if (shouldThrow) {
    shouldThrow = false;
    throw new Error('boom');
    }
    });
    try {
    spy();
    } catch {}
    spy();
    expect(spy, 'always threw');
    // AssertionError: Expected spy to always have thrown an exception

    Negation:

    const spy = sinon.spy();
    spy();
    expect(spy, 'not always threw');

    Asserts that the first spy was called before the second spy.

    Success:

    const first = sinon.spy();
    const second = sinon.spy();
    first();
    second();
    expect(first, 'was called before', second);

    Failure:

    const first = sinon.spy();
    const second = sinon.spy();
    second();
    first();
    expect(first, 'was called before', second);
    // AssertionError: Expected first spy to have been called before second spy

    Negation:

    const first = sinon.spy();
    const second = sinon.spy();
    second();
    first();
    expect(first, 'not was called before', second);

    Asserts that the first spy was called after the second spy.

    Success:

    const first = sinon.spy();
    const second = sinon.spy();
    second();
    first();
    expect(first, 'was called after', second);

    Failure:

    const first = sinon.spy();
    const second = sinon.spy();
    first();
    second();
    expect(first, 'was called after', second);
    // AssertionError: Expected first spy to have been called after second spy

    Negation:

    const first = sinon.spy();
    const second = sinon.spy();
    first();
    second();
    expect(first, 'not was called after', second);

    Asserts that a specific spy call had exactly the specified arguments.

    Access individual calls via spy.firstCall, spy.secondCall, spy.thirdCall, spy.lastCall, or spy.getCall(n).

    Success:

    const spy = sinon.spy();
    spy('foo', 42);
    expect(spy.firstCall, 'to have args', ['foo', 42]);

    Failure:

    const spy = sinon.spy();
    spy('foo', 42);
    expect(spy.firstCall, 'to have args', ['bar', 42]);
    // AssertionError: Expected spy call to have specified arguments

    Negation:

    const spy = sinon.spy();
    spy('foo', 42);
    expect(spy.firstCall, 'not to have args', ['bar', 42]);

    Asserts that a specific spy call returned the specified value.

    Success:

    const stub = sinon.stub().returns(100);
    stub();
    expect(stub.firstCall, 'to have returned', 100);

    Failure:

    const stub = sinon.stub().returns(100);
    stub();
    expect(stub.firstCall, 'to have returned', 200);
    // AssertionError: Expected spy call to have returned specified value

    Negation:

    const stub = sinon.stub().returns(100);
    stub();
    expect(stub.firstCall, 'not to have returned', 200);

    Asserts that a specific spy call threw an exception.

    Success:

    const spy = sinon.spy(() => {
    throw new Error('boom');
    });
    try {
    spy();
    } catch {}
    expect(spy.firstCall, 'to have thrown');

    Failure:

    const spy = sinon.spy();
    spy();
    expect(spy.firstCall, 'to have thrown');
    // AssertionError: Expected spy call to have thrown an exception

    Negation:

    const spy = sinon.spy();
    spy();
    expect(spy.firstCall, 'not to have thrown');

    Asserts that a specific spy call used the specified this context.

    Success:

    const obj = { name: 'test' };
    const spy = sinon.spy();
    spy.call(obj);
    expect(spy.firstCall, 'to have this', obj);

    Failure:

    const obj1 = { name: 'one' };
    const obj2 = { name: 'two' };
    const spy = sinon.spy();
    spy.call(obj1);
    expect(spy.firstCall, 'to have this', obj2);
    // AssertionError: Expected spy call to have specified this context

    Negation:

    const obj1 = { name: 'one' };
    const obj2 = { name: 'two' };
    const spy = sinon.spy();
    spy.call(obj1);
    expect(spy.firstCall, 'not to have this', obj2);

    Asserts that an array of spies were called in the specified order.

    Success:

    const first = sinon.spy();
    const second = sinon.spy();
    const third = sinon.spy();
    first();
    second();
    third();
    expect([first, second, third], 'given call order');

    Failure:

    const first = sinon.spy();
    const second = sinon.spy();
    const third = sinon.spy();
    third();
    first();
    second();
    expect([first, second, third], 'given call order');
    // AssertionError: Expected spies to have been called in order, but spy 0 was not called before spy 1

    Negation:

    const first = sinon.spy();
    const second = sinon.spy();
    const third = sinon.spy();
    third();
    first();
    second();
    expect([first, second, third], 'not given call order');

    Asserts that all calls to a spy match a specification array. Each element in the array corresponds to one call and can be either:

    • An object with optional args, returned, threw, thisValue properties
    • An array (shorthand for { args: [...] })

    The number of specifications must match the number of calls exactly.

    Success:

    const spy = sinon.spy();
    spy('a', 1);
    spy('b', 2);
    spy('c', 3);

    // Using object specifications
    expect(spy, 'to have calls satisfying', [
    { args: ['a', 1] },
    { args: ['b', 2] },
    { args: ['c', 3] },
    ]);

    // Using array shorthand
    expect(spy, 'to have calls satisfying', [
    ['a', 1],
    ['b', 2],
    ['c', 3],
    ]);

    With return values and this context:

    const obj = { multiplier: 2 };
    const stub = sinon.stub().callsFake(function (x) {
    return x * this.multiplier;
    });

    stub.call(obj, 5);
    stub.call(obj, 10);

    expect(stub, 'to have calls satisfying', [
    { args: [5], returned: 10, thisValue: obj },
    { args: [10], returned: 20, thisValue: obj },
    ]);

    Failure:

    const spy = sinon.spy();
    spy('a');
    spy('b');
    expect(spy, 'to have calls satisfying', [['a'], ['c']]);
    // AssertionError: Call 1: argument 0 did not match

    const spy2 = sinon.spy();
    spy2('a');
    expect(spy2, 'to have calls satisfying', [['a'], ['b']]);
    // AssertionError: Expected spy to have 2 call(s), but it had 1

    Negation:

    const spy = sinon.spy();
    spy('a');
    spy('b');
    expect(spy, 'not to have calls satisfying', [['x'], ['y']]);

    Copyright © 2026 Christopher "boneskull" Hiller. Licensed under BlueOak-1.0.0.