createStubInstance() doesn't receive per-sandbox callId context, breaking calledAfter/calledBefore vs spy()
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 9.8k
- Forks
- 809
- PR merge metrics
- No merged PRs in 30d
Description
Describe the bug
22.1.0 introduced per-sandbox callId isolation for parallel test support (#2472, fixed by #2715). Fakes created via sandbox.spy() / sandbox.stub() now get their callId from an isolated sandboxContext object via .withContext(...).
However, createStubInstance() (both the top-level sinon.createStubInstance() and sandbox.createStubInstance()) was not updated as part of that fix. It still calls the plain stub(stubInstance) internally (see create-stub-instance.js / stub.js), which never receives a context and therefore falls back to the hardcoded module-level singleton defaultContext = { callId: 0 } in proxy-invoke.js.
The practical effect: any fake created via createStubInstance() is tracked on a totally different callId counter than fakes created via spy() / stub(). Both counters start at 0 independently, so calledAfter() / calledBefore() / calledImmediatelyBefore() / calledImmediatelyAfter() comparisons between a createStubInstance()-created method and a spy()/stub()-created fake become meaningless — they no longer reflect real call order.
This worked correctly before 22.1.0, when there was a single global callId counter shared by everything.
To Reproduce
const sinon = require('sinon')
class Foo {
bar () {}
}
const spy1 = sinon.spy(() => {})
spy1() // called first
const stubInst = sinon.createStubInstance(Foo)
stubInst.bar() // called second, strictly after spy1()
console.log(stubInst.bar.calledAfter(spy1)) // => false (should be true)
console.log('spy1 callIds:', spy1.callIds) // [0]
console.log('stub.bar callIds:', stubInst.bar.callIds) // [0]
Both callIds are 0 even though stubInst.bar() ran strictly after spy1(), because they're tracked on two unrelated counter objects.
Expected behavior
stubInst.bar.calledAfter(spy1) should return true, matching real call order, the same as it did in sinon 22.0.0.
Context
- Sinon version: 22.1.0
- Runtime: Node.js (any)
Additional context
Likely fix: have createStubInstance() accept/forward a context argument the same way stub.withContext does, and have sandbox.createStubInstance() pass its own sandboxContext through, similar to how sandbox.spy() / sandbox.stub() already do.
I found this while investigating a test regression after bumping from sinon 22.0.0 to 22.1.0 — a test comparing call order between a sinon.createStubInstance()-based stub and a sinon.spy() started failing intermittently even though the real call order was correct. The repro above reduces it to the minimal case, with no project-specific code.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing createStubInstance() through create-stub-instance.js and stub.js, then compare its context handling with sandbox.spy() and sandbox.stub(). Run the minimal reproduction from the issue and verify that createStubInstance()-created methods share call ordering with spies, including calledAfter(), calledBefore(), and related comparisons.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100