Skip to content

stub.resolves(xxx) still applies after stub.callThrough() #2668

@Sigill

Description

@Sigill

Describe the bug

I have a test where a stub is initially configured to stub.resolve(xxx).
Later I stub.callThrough() to restore the the initial method.
However, when calling the stubbed method, the original method is not called, resolve(xxx) still applies.

To Reproduce

it("#xxx - callThrough not clearing resolves()", async function () {
    const ns = {
        async foo() { return 'bar'; }
    };

    const stub = sinon.stub(ns, 'foo');
    stub.resolves('baz');

    assert.equals(await ns.foo(), 'baz');

    stub.callThrough();

    assert.equals(await ns.foo(), 'bar'); // fails, stub resolves with 'baz'.
});

Note that the same test without the async/await is pass.

Expected behavior

The original method should have been called.

Context (please complete the following information):

  • Sinon version : 17.0.2 to 21.0.0
  • Runtime: Node 20
  • Output of npx envinfo --browsers --binaries:
  Binaries:
    Node: 20.17.0 - /home/cfx/.nvm/versions/node/v20.17.0/bin/node
    npm: 10.8.2 - /home/cfx/.nvm/versions/node/v20.17.0/bin/npm
    pnpm: 10.26.0 - /home/cfx/.nvm/versions/node/v20.17.0/bin/pnpm
  Browsers:
    Chrome: 127.0.6533.72
  • Other relevant environmental info:
  • Other libraries you are using:
  • Example URL:

Additional context
The issue appears to have been introduced by #2593, the issue does not occur with Sinon 17.0.1.

Inspired by various cleanup logic from default-behaviors.js, I've been able to quick fix the issue with:

diff --git a/lib/sinon/default-behaviors.js b/lib/sinon/default-behaviors.js
index ba85007e..76d64556 100644
--- a/lib/sinon/default-behaviors.js
+++ b/lib/sinon/default-behaviors.js
@@ -240,6 +240,14 @@ const defaultBehaviors = {
     },
 
     callThrough: function callThrough(fake) {
+        fake.returnValue = undefined;
+        fake.resolve = false;
+        fake.resolveThis = false;
+        fake.reject = false;
+        fake.returnValueDefined = false;
+        fake.exception = undefined;
+        fake.exceptionCreator = undefined;
+        fake.fakeFn = undefined;
         fake.callsThrough = true;
     },
 

However I'm not sure to understand why the cleanup logic varies so much from case to case in this file and what really needs to be cleaned-up, so this might not be correct/sufficient.

Seeing the implementation of callThroughWithNew() in default-behaviors.js, I'm wondering if it has the same issue.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions