npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bupkis/sinon

v0.2.0

Published

Sinon spy/stub/mock assertions for Bupkis

Readme

@bupkis/sinon

Sinon spy/stub/mock assertions for Bupkis.

Installation

npm install @bupkis/sinon bupkis sinon

Usage

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'],
]);

Assertions

{Spy} was called

✏️ 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');

{Spy} was not 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

{Spy} was called once

✏️ 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');

{Spy} was called twice

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');

{Spy} was called thrice

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');

{Spy} was called times {number}

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);

{Spy} to have returned

✏️ 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');

{Spy} to have returned times {number}

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);

{Spy} to have returned with {unknown}

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);

{Spy} was called with {array}

✏️ 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']);

{Spy} was always called with {array}

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']);

{Spy} was called with exactly {array}

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]);

{Spy} was never called with {array}

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']);

{Spy} was called on {unknown}

✏️ 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);

{Spy} was always called on {unknown}

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);

{Spy} threw

✏️ 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');

{Spy} threw {Error | string}

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');

{Spy} always threw

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');

{Spy} was called before {Spy}

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);

{Spy} was called after {Spy}

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);

{SpyCall} to have args {array}

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]);

{SpyCall} to have returned {unknown}

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);

{SpyCall} to have thrown

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');

{SpyCall} to have this {unknown}

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);

{Spy[]} given call order

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');

{Spy} to have calls satisfying {array}

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']]);

License

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