diff --git a/lib/sinon/stub.js b/lib/sinon/stub.js index 8b9e32af7..1509642d3 100644 --- a/lib/sinon/stub.js +++ b/lib/sinon/stub.js @@ -131,12 +131,10 @@ stub.createStubInstance = function (constructor, overrides) { throw new TypeError("The constructor should be a function."); } - // eslint-disable-next-line no-empty-function - const noop = () => {}; - const defaultNoOpInstance = Object.create(constructor.prototype); - walkObject((obj, prop) => (obj[prop] = noop), defaultNoOpInstance); + const stubInstance = Object.create(constructor.prototype); + stubInstance[Symbol.for("SinonType")] = "StubInstance"; - const stubbedObject = stub(defaultNoOpInstance); + const stubbedObject = stub(stubInstance); forEach(Object.keys(overrides || {}), function (propertyName) { if (propertyName in stubbedObject) { diff --git a/lib/sinon/util/core/wrap-method.js b/lib/sinon/util/core/wrap-method.js index fc2cd4815..c5e421c8e 100644 --- a/lib/sinon/util/core/wrap-method.js +++ b/lib/sinon/util/core/wrap-method.js @@ -1,5 +1,7 @@ "use strict"; +// eslint-disable-next-line no-empty-function +const noop = () => {}; var getPropertyDescriptor = require("./get-property-descriptor"); var extend = require("./extend"); var hasOwnProperty = @@ -230,6 +232,11 @@ module.exports = function wrapMethod(object, property, method) { } } } + if (object[Symbol.for("SinonType")] === "StubInstance") { + // this is simply to avoid errors after restoring if something should + // traverse the object in a cleanup phase, ref #2477 + object[property] = noop(); + } } return method; diff --git a/test/issues/issues-test.js b/test/issues/issues-test.js index aa80f8f57..ea3a1993f 100644 --- a/test/issues/issues-test.js +++ b/test/issues/issues-test.js @@ -805,4 +805,21 @@ describe("issues", function () { assert.isUndefined(restoredPropertyDescriptor); }); }); + + describe("#2501 - createStubInstance stubs are not able to call through to the underlying function on the prototype", function () { + it("should be able call through to the underlying function on the prototype", function () { + class Foo { + testMethod() { + this.wasCalled = true; + return 42; + } + } + + const fooStubInstance = this.sandbox.createStubInstance(Foo); + fooStubInstance.testMethod.callThrough(); + // const fooStubInstance = new Foo() + fooStubInstance.testMethod(); + // assert.isTrue(fooStubInstance.wasCalled); + }); + }); });