diff --git a/lib/sinon/spy.js b/lib/sinon/spy.js index 3c5f70336..b91a5491b 100644 --- a/lib/sinon/spy.js +++ b/lib/sinon/spy.js @@ -152,7 +152,26 @@ function createProxy(func, proxyLength) { return p.invoke(func, this, slice(arguments)); }; } - extend.nonEnum(p, { isSinonProxy: true }); + extend.nonEnum(p, { + isSinonProxy: true, + + called: false, + notCalled: true, + calledOnce: false, + calledTwice: false, + calledThrice: false, + callCount: 0, + firstCall: null, + secondCall: null, + thirdCall: null, + lastCall: null, + args: [], + returnValues: [], + thisValues: [], + exceptions: [], + callIds: [], + errorsWithCallStack: [] + }); return p; } @@ -172,24 +191,22 @@ var spyApi = { throw err; } - extend.nonEnum(this, { - called: false, - notCalled: true, - calledOnce: false, - calledTwice: false, - calledThrice: false, - callCount: 0, - firstCall: null, - secondCall: null, - thirdCall: null, - lastCall: null, - args: [], - returnValues: [], - thisValues: [], - exceptions: [], - callIds: [], - errorsWithCallStack: [] - }); + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.errorsWithCallStack = []; if (this.fakes) { forEach(this.fakes, function(fake) { @@ -221,7 +238,7 @@ var spyApi = { extend.nonEnum(proxy, spy); delete proxy.create; - extend.nonEnum(proxy, funk); + extend(proxy, funk); proxy.resetHistory(); proxy.prototype = funk.prototype; @@ -391,7 +408,7 @@ var spyApi = { return matching; } } else { - this.fakes = []; + extend.nonEnum(this, { fakes: [] }); } var original = this; diff --git a/test/spy-test.js b/test/spy-test.js index dc75661c4..8822512bc 100644 --- a/test/spy-test.js +++ b/test/spy-test.js @@ -2851,4 +2851,62 @@ describe("spy", function() { } }); }); + + describe("non enumerable properties", function() { + it("create and call spy apis", function() { + var spy = createSpy(); + assert.equals(Object.keys(spy), []); + + // call spy and verify no enumerable properties are added + spy(15); + assert.equals(Object.keys(spy), []); + + // it should still work to add properties + spy.fooBar = 1; + assert.equals(Object.keys(spy), ["fooBar"]); + + // call some spy APIs and verify no enumerable properties are added + spy.withArgs(1); + // eslint-disable-next-line no-unused-vars + var val = spy.called; + spy.calledBefore(createSpy()); + spy.calledAfter(createSpy()); + spy.calledOn(undefined); + spy.calledWith(15); + spy.calledWithNew(); + spy.threw(); + spy.returned("ret"); + val = spy.thisValues.length; + val = spy.exceptions.length; + val = spy.returnValues.length; + assert.equals(Object.keys(spy), ["fooBar"]); + + // verify that reset history doesn't change enumerable properties + spy.resetHistory(); + assert.equals(Object.keys(spy), ["fooBar"]); + }); + + it("create spy from function", function() { + var func = function() { + throw new Error("aError"); + }; + func.aProp = 42; + var spy = createSpy.create(func); + + assert.equals(spy.aProp, 42); + assert.equals(Object.keys(spy), Object.keys(func)); + assert.equals(Object.keys(spy), ["aProp"]); + + // eslint-disable-next-line no-restricted-syntax + try { + spy(); + } catch (e) { + // empty + } + spy.threw(); + + spy.resetHistory(); + assert.equals(Object.keys(spy), ["aProp"]); + }); + }); });