{"_id":"@bupkis/sinon","_rev":"3-bc4c0679d9025e6b6cfc0b45d2cf1946","name":"@bupkis/sinon","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.1":{"name":"@bupkis/sinon","version":"0.1.1","keywords":["bupkis","sinon","spy","stub","mock","assert","assertion","test"],"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","_id":"@bupkis/sinon@0.1.1","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"homepage":"https://github.com/boneskull/bupkis#readme","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"dist":{"shasum":"8f173860f5924bb0083eeaf997e5b717533aafa5","tarball":"https://registry.npmjs.org/@bupkis/sinon/-/sinon-0.1.1.tgz","fileCount":40,"integrity":"sha512-1+IiADiuvrqxW/lxDMvegnWS7JK1oNxZVfSOqg46LvMDDzFvEjgHVwtcuQHsVGL0Ypv9HdPa3D0EnorzGUFDvg==","signatures":[{"sig":"MEUCIQCRg/+EhUIDHtN3GqA1VQ5NMRg1lykPHwu2+59HkJ+Z1wIgYEKVSvxuuSMRhf4xDhbo3eBDFFlXjwVIWFGvYcYmx3c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":159048},"main":"./dist/index.cjs","type":"module","zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"types":"./dist/index.d.cts","module":"./dist/index.js","engines":{"node":"^20.19.0 || ^22.12.0 || >=23"},"exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"ca2eb98497de0200e5861cb8b7de9d48ca26439d","scripts":{"test":"npm run test:base -- \"test/*.test.ts\"","build":"zshy","test:dev":"npm run test:base -- --watch \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:types":"exit 0","test:node20":"npm run test:base -- test/*.test.ts","test:property":"npm run test:base -- ./test/property.test.ts","prepublishOnly":"npm run build"},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"prettier":{"tsdoc":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg","prettier-plugin-sort-json"],"singleQuote":true,"jsdocPreferCodeFences":true,"jsdocCommentLineStrategy":"keep"},"repository":{"url":"git+https://github.com/boneskull/bupkis.git","type":"git","directory":"packages/sinon"},"_npmVersion":"11.6.2","description":"Sinon spy/stub/mock assertions for Bupkis","directories":{},"_nodeVersion":"24.12.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sinon":"21.0.1","fast-check":"4.5.2","@types/sinon":"21.0.0"},"peerDependencies":{"sinon":">=17.0.0","bupkis":">=0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/sinon_0.1.1_1768432873269_0.08890422058117853","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bupkis/sinon","version":"0.2.0","keywords":["bupkis","sinon","spy","stub","mock","assert","assertion","test"],"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","_id":"@bupkis/sinon@0.2.0","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"homepage":"https://github.com/boneskull/bupkis#readme","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"dist":{"shasum":"64bdc6d1419e3248583781089f13433610c57647","tarball":"https://registry.npmjs.org/@bupkis/sinon/-/sinon-0.2.0.tgz","fileCount":40,"integrity":"sha512-epVGFYcW1ou7e9DQTBYo8tHE5QU3o/PxbR2XN4Z6CLx8aRW9yidsvn1+XPz0gdtbZO+oxlXagpXLfa+fIEcdTQ==","signatures":[{"sig":"MEUCIFpl96x7GDntkAX9PycvakNeCbcne/Zvxx+wHBGCz6DaAiEA0qNJNObIVU9AliG6CgHQc48kJtL1N/aJkiNu933OJGw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173262},"main":"./dist/index.cjs","type":"module","zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"types":"./dist/index.d.cts","module":"./dist/index.js","engines":{"node":"^20.19.0 || ^22.12.0 || >=23"},"exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"f09ff1cfb21ceb03dee850971611a3d1e98086b4","scripts":{"test":"npm run test:base -- \"test/*.test.ts\"","build":"zshy","test:dev":"npm run test:base -- --watch \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:types":"exit 0","test:node20":"npm run test:base -- test/*.test.ts","test:property":"npm run test:base -- ./test/property.test.ts","prepublishOnly":"npm run build"},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"prettier":{"tsdoc":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg"],"singleQuote":true,"jsdocPreferCodeFences":true,"jsdocCommentLineStrategy":"keep"},"repository":{"url":"git+https://github.com/boneskull/bupkis.git","type":"git","directory":"packages/sinon"},"_npmVersion":"11.6.2","description":"Sinon spy/stub/mock assertions for Bupkis","directories":{},"_nodeVersion":"24.12.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sinon":"21.0.1","fast-check":"4.5.2","@types/sinon":"21.0.0"},"peerDependencies":{"sinon":">=17.0.0","bupkis":">=0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/sinon_0.2.0_1768604276962_0.6500355624081966","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@bupkis/sinon","version":"1.0.0","type":"module","description":"Sinon spy/stub/mock assertions for Bupkis","repository":{"directory":"packages/sinon","type":"git","url":"git+https://github.com/boneskull/bupkis.git"},"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","engines":{"node":"^22.13.0 || >=24.0.0"},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"keywords":["bupkis","sinon","spy","stub","mock","assert","assertion","test"],"scripts":{"build":"zshy","prepublishOnly":"npm run build","test":"npm run test:base -- \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:ci":"npm run test -- --test-reporter=node-test-github-reporter","test:coverage":"c8 --reporter=lcov --reporter=text npm test","test:dev":"npm run test -- --watch","test:property":"npm run test:base -- test/property.test.ts"},"peerDependencies":{"bupkis":">=0.15.0","sinon":">=17.0.0"},"devDependencies":{"@types/sinon":"21.0.1","sinon":"22.0.0"},"publishConfig":{"access":"public"},"prettier":{"jsdocCommentLineStrategy":"keep","jsdocPreferCodeFences":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg"],"singleQuote":true,"tsdoc":true},"zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"gitHead":"c0f3fcd74ca0f7e307d2a68ce12bba81b4059eb5","_id":"@bupkis/sinon@1.0.0","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"homepage":"https://github.com/boneskull/bupkis#readme","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-XyksZBQxZY7mnBF/x68EySYFzSADv4dxDpCSqSwx6cdFpTRxHyQ5WdtASlL2oKGzqc41Gi2AX54JggakbUkvWQ==","shasum":"d5b309366e0be6f71d2f2b753b8f38d21f60f694","tarball":"https://registry.npmjs.org/@bupkis/sinon/-/sinon-1.0.0.tgz","fileCount":40,"unpackedSize":173864,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDxBHxCwC9Osxtq5VRpfqqySKgcuR7EkHf/ilAoc3Vc+AiBsSZyzgXss1b7mbymtzGO9RmGA6WieLCoB39D2BEzAQQ=="}]},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"directories":{},"maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sinon_1.0.0_1783650195127_0.8846664260165507"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-14T23:21:13.133Z","modified":"2026-07-10T02:23:15.418Z","0.1.1":"2026-01-14T23:21:13.688Z","0.2.0":"2026-01-16T22:57:57.178Z","1.0.0":"2026-07-10T02:23:15.275Z"},"bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","homepage":"https://github.com/boneskull/bupkis#readme","keywords":["bupkis","sinon","spy","stub","mock","assert","assertion","test"],"repository":{"directory":"packages/sinon","type":"git","url":"git+https://github.com/boneskull/bupkis.git"},"description":"Sinon spy/stub/mock assertions for Bupkis","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"readme":"# @bupkis/sinon\n\nSinon spy/stub/mock assertions for [**BUPKIS**][bupkis].\n\n## Installation\n\n```bash\nnpm install @bupkis/sinon bupkis sinon\n```\n\n## Usage\n\n```typescript\nimport { use } from 'bupkis';\nimport sinonAssertions from '@bupkis/sinon';\nimport sinon from 'sinon';\n\nconst { expect } = use(sinonAssertions);\n\n// Basic spy assertions\nconst spy = sinon.spy();\nspy(42);\nexpect(spy, 'was called');\nexpect(spy, 'was called once');\nexpect(spy, 'was called with', [42]);\n\n// Call count\nspy();\nspy();\nexpect(spy, 'was called times', 3);\n\n// Stub return values\nconst stub = sinon.stub().returns(100);\nstub();\nexpect(stub.firstCall, 'to have returned', 100);\n\n// Call order\nconst first = sinon.spy();\nconst second = sinon.spy();\nfirst();\nsecond();\nexpect(first, 'was called before', second);\nexpect([first, second], 'given call order');\n\n// Complex call specifications\nconst logger = sinon.spy();\nlogger('info', 'started');\nlogger('debug', 'processing');\nlogger('info', 'done');\nexpect(logger, 'to have calls satisfying', [\n  ['info', 'started'],\n  ['debug', 'processing'],\n  ['info', 'done'],\n]);\n```\n\n## Assertions\n\n### {Spy} was called\n\n> ✏️ Aliases:\n>\n>     {Spy} was called\n>     {Spy} to have been called\n\nAsserts that a spy was called at least once.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'was called');\nexpect(spy, 'to have been called');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nexpect(spy, 'was called');\n// AssertionError: Expected spy to have been called, but it was never called\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nexpect(spy, 'not to have been called');\n```\n\n### {Spy} was not called\n\n> ✏️ Aliases:\n>\n>     {Spy} was not called\n>     {Spy} to not have been called\n\nAsserts that a spy was never called.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nexpect(spy, 'was not called');\nexpect(spy, 'to not have been called');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'was not called');\n// AssertionError: Expected spy to not have been called, but it was called 1 time(s)\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'not was not called'); // awkward but valid\n```\n\n### {Spy} was called once\n\n> ✏️ Aliases:\n>\n>     {Spy} was called once\n>     {Spy} to have been called once\n\nAsserts that a spy was called exactly once.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'was called once');\nexpect(spy, 'to have been called once');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nspy();\nexpect(spy, 'was called once');\n// AssertionError: Expected spy to have been called exactly once\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nexpect(spy, 'not to have been called once');\n```\n\n### {Spy} was called twice\n\nAsserts that a spy was called exactly twice.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy();\nspy();\nexpect(spy, 'was called twice');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'was called twice');\n// AssertionError: Expected spy to have been called exactly twice\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nexpect(spy, 'not was called twice');\n```\n\n### {Spy} was called thrice\n\nAsserts that a spy was called exactly three times.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy();\nspy();\nspy();\nexpect(spy, 'was called thrice');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nspy();\nexpect(spy, 'was called thrice');\n// AssertionError: Expected spy to have been called exactly three times\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nexpect(spy, 'not was called thrice');\n```\n\n### {Spy} was called times {number}\n\nAsserts that a spy was called exactly the specified number of times.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy();\nspy();\nspy();\nspy();\nspy();\nexpect(spy, 'was called times', 5);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nspy();\nexpect(spy, 'was called times', 5);\n// AssertionError: Expected spy to have been called 5 time(s)\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'not was called times', 5);\n```\n\n### {Spy} to have returned\n\n> ✏️ Aliases:\n>\n>     {Spy} to have returned\n>     {Spy} returned\n\nAsserts that a spy returned successfully (without throwing) at least once.\n\n**Success**:\n\n```js\nconst spy = sinon.spy(() => 42);\nspy();\nexpect(spy, 'to have returned');\nexpect(spy, 'returned');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy, 'to have returned');\n// AssertionError: Expected spy to have returned at least once\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy, 'not to have returned');\n```\n\n### {Spy} to have returned times {number}\n\nAsserts that a spy returned successfully exactly the specified number of times.\n\n**Success**:\n\n```js\nconst spy = sinon.spy(() => 42);\nspy();\nspy();\nspy();\nexpect(spy, 'to have returned times', 3);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy(() => 42);\nspy();\nspy();\nexpect(spy, 'to have returned times', 3);\n// AssertionError: Expected spy to have returned 3 time(s), but it returned 2 time(s)\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy(() => 42);\nspy();\nexpect(spy, 'not to have returned times', 3);\n```\n\n### {Spy} to have returned with {unknown}\n\nAsserts that a spy returned the specified value on at least one call. Uses Sinon's `spy.returned()` method for comparison (deep equality via samsam).\n\n**Success**:\n\n```js\nconst spy = sinon.spy((x) => x * 2);\nspy(5);\nspy(10);\nexpect(spy, 'to have returned with', 10);\nexpect(spy, 'to have returned with', 20);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy((x) => x * 2);\nspy(5);\nexpect(spy, 'to have returned with', 100);\n// AssertionError: Expected spy to have returned specified value\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy((x) => x * 2);\nspy(5);\nexpect(spy, 'not to have returned with', 100);\n```\n\n### {Spy} was called with {array}\n\n> ✏️ Aliases:\n>\n>     {Spy} was called with {array}\n>     {Spy} to have been called with {array}\n\nAsserts 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.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42, 'extra');\nexpect(spy, 'was called with', ['foo', 42]); // prefix match - 'extra' ignored\nexpect(spy, 'to have been called with', ['foo', 42, 'extra']); // exact match\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy('bar');\nexpect(spy, 'was called with', ['foo']);\n// AssertionError: Expected spy to have been called with specified arguments\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy('bar');\nexpect(spy, 'not to have been called with', ['foo']);\n```\n\n### {Spy} was always called with {array}\n\nAsserts that _all_ calls to the spy included the specified arguments (prefix match).\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 1);\nspy('foo', 2);\nspy('foo', 3);\nexpect(spy, 'was always called with', ['foo']);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy('foo');\nspy('bar');\nexpect(spy, 'was always called with', ['foo']);\n// AssertionError: Expected spy to always have been called with specified arguments\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy('foo');\nspy('bar');\nexpect(spy, 'not was always called with', ['foo']);\n```\n\n### {Spy} was called with exactly {array}\n\nAsserts that at least one call to the spy had _exactly_ the specified arguments (no additional arguments).\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42);\nexpect(spy, 'was called with exactly', ['foo', 42]);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42, 'extra');\nexpect(spy, 'was called with exactly', ['foo', 42]);\n// AssertionError: Expected spy to have been called with exactly the specified arguments\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42, 'extra');\nexpect(spy, 'not was called with exactly', ['foo', 42]);\n```\n\n### {Spy} was never called with {array}\n\nAsserts that the spy was never called with the specified arguments.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy('foo');\nspy('bar');\nexpect(spy, 'was never called with', ['baz']);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy('foo');\nexpect(spy, 'was never called with', ['foo']);\n// AssertionError: Expected spy to never have been called with specified arguments\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy('foo');\nexpect(spy, 'not was never called with', ['foo']);\n```\n\n### {Spy} was called on {unknown}\n\n> ✏️ Aliases:\n>\n>     {Spy} was called on {unknown}\n>     {Spy} to have been called on {unknown}\n\nAsserts that at least one call to the spy used the specified `this` context.\n\n**Success**:\n\n```js\nconst obj = { name: 'test' };\nconst spy = sinon.spy();\nspy.call(obj);\nexpect(spy, 'was called on', obj);\nexpect(spy, 'to have been called on', obj);\n```\n\n**Failure**:\n\n```js\nconst obj1 = { name: 'one' };\nconst obj2 = { name: 'two' };\nconst spy = sinon.spy();\nspy.call(obj1);\nexpect(spy, 'was called on', obj2);\n// AssertionError: Expected spy to have been called with specified this context\n```\n\n**Negation**:\n\n```js\nconst obj1 = { name: 'one' };\nconst obj2 = { name: 'two' };\nconst spy = sinon.spy();\nspy.call(obj1);\nexpect(spy, 'not to have been called on', obj2);\n```\n\n### {Spy} was always called on {unknown}\n\nAsserts that _all_ calls to the spy used the specified `this` context.\n\n**Success**:\n\n```js\nconst obj = { name: 'test' };\nconst spy = sinon.spy();\nspy.call(obj);\nspy.call(obj);\nexpect(spy, 'was always called on', obj);\n```\n\n**Failure**:\n\n```js\nconst obj1 = { name: 'one' };\nconst obj2 = { name: 'two' };\nconst spy = sinon.spy();\nspy.call(obj1);\nspy.call(obj2);\nexpect(spy, 'was always called on', obj1);\n// AssertionError: Expected spy to always have been called with specified this context\n```\n\n**Negation**:\n\n```js\nconst obj1 = { name: 'one' };\nconst obj2 = { name: 'two' };\nconst spy = sinon.spy();\nspy.call(obj1);\nspy.call(obj2);\nexpect(spy, 'not was always called on', obj1);\n```\n\n### {Spy} threw\n\n> ✏️ Aliases:\n>\n>     {Spy} threw\n>     {Spy} to have thrown\n\nAsserts that the spy threw an exception on at least one call.\n\n**Success**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy, 'threw');\nexpect(spy, 'to have thrown');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'threw');\n// AssertionError: Expected spy to have thrown an exception\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'not to have thrown');\n```\n\n### {Spy} threw {Error | string}\n\nAsserts that the spy threw a specific error. The parameter can be an `Error` instance or a string representing the error type name.\n\n**Success**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new TypeError('bad type');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy, 'threw', 'TypeError'); // match by type name\nexpect(spy, 'threw', new TypeError('bad type')); // match by instance\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy, 'threw', 'TypeError');\n// AssertionError: Expected spy to have thrown specified exception\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy, 'not threw', 'TypeError');\n```\n\n### {Spy} always threw\n\nAsserts that the spy threw an exception on every call.\n\n**Success**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\ntry {\n  spy();\n} catch {}\nexpect(spy, 'always threw');\n```\n\n**Failure**:\n\n```js\nlet shouldThrow = true;\nconst spy = sinon.spy(() => {\n  if (shouldThrow) {\n    shouldThrow = false;\n    throw new Error('boom');\n  }\n});\ntry {\n  spy();\n} catch {}\nspy();\nexpect(spy, 'always threw');\n// AssertionError: Expected spy to always have thrown an exception\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy, 'not always threw');\n```\n\n### {Spy} was called before {Spy}\n\nAsserts that the first spy was called before the second spy.\n\n**Success**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nfirst();\nsecond();\nexpect(first, 'was called before', second);\n```\n\n**Failure**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nsecond();\nfirst();\nexpect(first, 'was called before', second);\n// AssertionError: Expected first spy to have been called before second spy\n```\n\n**Negation**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nsecond();\nfirst();\nexpect(first, 'not was called before', second);\n```\n\n### {Spy} was called after {Spy}\n\nAsserts that the first spy was called after the second spy.\n\n**Success**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nsecond();\nfirst();\nexpect(first, 'was called after', second);\n```\n\n**Failure**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nfirst();\nsecond();\nexpect(first, 'was called after', second);\n// AssertionError: Expected first spy to have been called after second spy\n```\n\n**Negation**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nfirst();\nsecond();\nexpect(first, 'not was called after', second);\n```\n\n### {SpyCall} to have args {array}\n\nAsserts that a specific spy call had exactly the specified arguments.\n\nAccess individual calls via `spy.firstCall`, `spy.secondCall`, `spy.thirdCall`, `spy.lastCall`, or `spy.getCall(n)`.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42);\nexpect(spy.firstCall, 'to have args', ['foo', 42]);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42);\nexpect(spy.firstCall, 'to have args', ['bar', 42]);\n// AssertionError: Expected spy call to have specified arguments\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy('foo', 42);\nexpect(spy.firstCall, 'not to have args', ['bar', 42]);\n```\n\n### {SpyCall} to have returned {unknown}\n\nAsserts that a specific spy call returned the specified value.\n\n**Success**:\n\n```js\nconst stub = sinon.stub().returns(100);\nstub();\nexpect(stub.firstCall, 'to have returned', 100);\n```\n\n**Failure**:\n\n```js\nconst stub = sinon.stub().returns(100);\nstub();\nexpect(stub.firstCall, 'to have returned', 200);\n// AssertionError: Expected spy call to have returned specified value\n```\n\n**Negation**:\n\n```js\nconst stub = sinon.stub().returns(100);\nstub();\nexpect(stub.firstCall, 'not to have returned', 200);\n```\n\n### {SpyCall} to have thrown\n\nAsserts that a specific spy call threw an exception.\n\n**Success**:\n\n```js\nconst spy = sinon.spy(() => {\n  throw new Error('boom');\n});\ntry {\n  spy();\n} catch {}\nexpect(spy.firstCall, 'to have thrown');\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy.firstCall, 'to have thrown');\n// AssertionError: Expected spy call to have thrown an exception\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy();\nexpect(spy.firstCall, 'not to have thrown');\n```\n\n### {SpyCall} to have this {unknown}\n\nAsserts that a specific spy call used the specified `this` context.\n\n**Success**:\n\n```js\nconst obj = { name: 'test' };\nconst spy = sinon.spy();\nspy.call(obj);\nexpect(spy.firstCall, 'to have this', obj);\n```\n\n**Failure**:\n\n```js\nconst obj1 = { name: 'one' };\nconst obj2 = { name: 'two' };\nconst spy = sinon.spy();\nspy.call(obj1);\nexpect(spy.firstCall, 'to have this', obj2);\n// AssertionError: Expected spy call to have specified this context\n```\n\n**Negation**:\n\n```js\nconst obj1 = { name: 'one' };\nconst obj2 = { name: 'two' };\nconst spy = sinon.spy();\nspy.call(obj1);\nexpect(spy.firstCall, 'not to have this', obj2);\n```\n\n### {Spy[]} given call order\n\nAsserts that an array of spies were called in the specified order.\n\n**Success**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nconst third = sinon.spy();\nfirst();\nsecond();\nthird();\nexpect([first, second, third], 'given call order');\n```\n\n**Failure**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nconst third = sinon.spy();\nthird();\nfirst();\nsecond();\nexpect([first, second, third], 'given call order');\n// AssertionError: Expected spies to have been called in order, but spy 0 was not called before spy 1\n```\n\n**Negation**:\n\n```js\nconst first = sinon.spy();\nconst second = sinon.spy();\nconst third = sinon.spy();\nthird();\nfirst();\nsecond();\nexpect([first, second, third], 'not given call order');\n```\n\n### {Spy} to have calls satisfying {array}\n\nAsserts that all calls to a spy match a specification array. Each element in the array corresponds to one call and can be either:\n\n- An **object** with optional `args`, `returned`, `threw`, `thisValue` properties\n- An **array** (shorthand for `{ args: [...] }`)\n\nThe number of specifications must match the number of calls exactly.\n\n**Success**:\n\n```js\nconst spy = sinon.spy();\nspy('a', 1);\nspy('b', 2);\nspy('c', 3);\n\n// Using object specifications\nexpect(spy, 'to have calls satisfying', [\n  { args: ['a', 1] },\n  { args: ['b', 2] },\n  { args: ['c', 3] },\n]);\n\n// Using array shorthand\nexpect(spy, 'to have calls satisfying', [\n  ['a', 1],\n  ['b', 2],\n  ['c', 3],\n]);\n```\n\n**With return values and `this` context**:\n\n```js\nconst obj = { multiplier: 2 };\nconst stub = sinon.stub().callsFake(function (x) {\n  return x * this.multiplier;\n});\n\nstub.call(obj, 5);\nstub.call(obj, 10);\n\nexpect(stub, 'to have calls satisfying', [\n  { args: [5], returned: 10, thisValue: obj },\n  { args: [10], returned: 20, thisValue: obj },\n]);\n```\n\n**Failure**:\n\n```js\nconst spy = sinon.spy();\nspy('a');\nspy('b');\nexpect(spy, 'to have calls satisfying', [['a'], ['c']]);\n// AssertionError: Call 1: argument 0 did not match\n\nconst spy2 = sinon.spy();\nspy2('a');\nexpect(spy2, 'to have calls satisfying', [['a'], ['b']]);\n// AssertionError: Expected spy to have 2 call(s), but it had 1\n```\n\n**Negation**:\n\n```js\nconst spy = sinon.spy();\nspy('a');\nspy('b');\nexpect(spy, 'not to have calls satisfying', [['x'], ['y']]);\n```\n\n## License\n\nCopyright © 2026 [Christopher \"boneskull\" Hiller][boneskull]. Licensed under [BlueOak-1.0.0](https://blueoakcouncil.org/license/1.0.0).\n\n[boneskull]: https://github.com/boneskull\n[bupkis]: https://bupkis.zip\n","readmeFilename":"README.md"}