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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions