From dbefd454364614ccae4456616e4fab6f94be2a22 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Sun, 21 May 2017 13:56:21 -0300 Subject: [PATCH 1/5] Name restore function on stubNonFunctionProperty --- lib/sinon/stub-non-function-property.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinon/stub-non-function-property.js b/lib/sinon/stub-non-function-property.js index ae534ccf1..1ea184ae5 100644 --- a/lib/sinon/stub-non-function-property.js +++ b/lib/sinon/stub-non-function-property.js @@ -13,7 +13,7 @@ function stubNonFunctionProperty(object, property, value) { object[property] = value; return { - restore: function () { + restore: function restore() { object[property] = original; } }; From c89fb21b00cd4f3d99fdeb48d2c5193f96d5e9fb Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Sun, 21 May 2017 13:58:20 -0300 Subject: [PATCH 2/5] Deprecate stubbing sandbox value in favor of value behavior --- lib/sinon/collection.js | 12 +++---- lib/sinon/default-behaviors.js | 19 +++++++++++ lib/sinon/sandbox-stub.js | 51 ++++++++++++++++++++++++++++ test/sandbox-test.js | 62 ++++++++++++++++++++++++++++++++++ test/stub-test.js | 22 ++++++++++++ 5 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 lib/sinon/sandbox-stub.js diff --git a/lib/sinon/collection.js b/lib/sinon/collection.js index a41575a3d..bda872a57 100644 --- a/lib/sinon/collection.js +++ b/lib/sinon/collection.js @@ -3,9 +3,8 @@ var sinonSpy = require("./spy"); var sinonStub = require("./stub"); var sinonMock = require("./mock"); -var throwOnFalsyObject = require("./throw-on-falsy-object"); +var sandboxStub = require("./sandbox-stub"); var collectOwnMethods = require("./collect-own-methods"); -var stubNonFunctionProperty = require("./stub-non-function-property"); var push = [].push; @@ -81,13 +80,12 @@ var collection = { }, stub: function stub(object, property/*, value*/) { - throwOnFalsyObject.apply(null, arguments); + if (arguments.length > 2) { + return sandboxStub.apply(this, arguments); + } + var stubbed = sinonStub.apply(null, arguments); var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object"; - var isStubbingNonFunctionProperty = property && typeof object[property] !== "function"; - var stubbed = isStubbingNonFunctionProperty ? - stubNonFunctionProperty.apply(null, arguments) : - sinonStub.apply(null, arguments); if (isStubbingEntireObject) { var ownMethods = collectOwnMethods(stubbed); diff --git a/lib/sinon/default-behaviors.js b/lib/sinon/default-behaviors.js index f5bdc46e2..c965cf20c 100644 --- a/lib/sinon/default-behaviors.js +++ b/lib/sinon/default-behaviors.js @@ -1,4 +1,5 @@ "use strict"; +var getPropertyDescriptor = require("./util/core/get-property-descriptor"); var slice = [].slice; var useLeftMostCallback = -1; @@ -186,6 +187,24 @@ module.exports = { set: setterFunction }); + return fake; + }, + + value: function value(fake, newVal) { + var rootStub = fake.stub || fake; + + var oldVal = getPropertyDescriptor(rootStub.rootObj, rootStub.propName).value; + + Object.defineProperty(rootStub.rootObj, rootStub.propName, { + value: newVal + }); + + fake.restore = function restore() { + Object.defineProperty(rootStub.rootObj, rootStub.propName, { + value: oldVal + }); + }; + return fake; } }; diff --git a/lib/sinon/sandbox-stub.js b/lib/sinon/sandbox-stub.js new file mode 100644 index 000000000..a120112f5 --- /dev/null +++ b/lib/sinon/sandbox-stub.js @@ -0,0 +1,51 @@ +"use strict"; + +var collectOwnMethods = require("./collect-own-methods"); +var deprecated = require("./util/core/deprecated"); +var getPropertyDescriptor = require("./util/core/get-property-descriptor"); +var stubNonFunctionProperty = require("./stub-non-function-property"); +var sinonStub = require("./stub"); +var throwOnFalsyObject = require("./throw-on-falsy-object"); + +// This is deprecated and will be removed in a future version of sinon. +// We will only consider pull requests that fix serious bugs in the implementation +function sandboxStub(object, property/*, value*/) { + deprecated.printWarning( + "sandbox.stub(obj, 'meth', val) is deprecated and will be removed from " + + "the public API in a future version of sinon." + + "\n Use sandbox(obj, 'meth').callsFake(fn) instead in order to stub a function." + + "\n Use sandbox(obj, 'meth').value(fn) instead in order to stub a non-function value." + ); + + throwOnFalsyObject.apply(null, arguments); + + var actualDescriptor = getPropertyDescriptor(object, property); + var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object"; + var isStubbingNonFuncProperty = typeof object === "object" + && typeof property !== "undefined" + && (typeof actualDescriptor === "undefined" + || typeof actualDescriptor.value !== "function"); + + + // When passing a value as third argument it will be applied to stubNonFunctionProperty + var stubbed = isStubbingNonFuncProperty ? + stubNonFunctionProperty.apply(null, arguments) : + sinonStub.apply(null, arguments); + + if (isStubbingEntireObject) { + var ownMethods = collectOwnMethods(stubbed); + ownMethods.forEach(this.add.bind(this)); + if (this.promiseLibrary) { + ownMethods.forEach(this.addUsingPromise.bind(this)); + } + } else { + this.add(stubbed); + if (this.promiseLibrary) { + stubbed.usingPromise(this.promiseLibrary); + } + } + + return stubbed; +} + +module.exports = sandboxStub; diff --git a/test/sandbox-test.js b/test/sandbox-test.js index d512bd51d..b3176c9a5 100644 --- a/test/sandbox-test.js +++ b/test/sandbox-test.js @@ -548,4 +548,66 @@ describe("sinonSandbox", function () { sandbox.restore(); }); }); + + describe("getters and setters", function () { + it("allows stubbing getters", function () { + var object = { + foo: "bar" + }; + + var sandbox = sinonSandbox.create(); + sandbox.stub(object, "foo").get(function () { + return "baz"; + }); + + assert.equals(object.foo, "baz"); + }); + + it("allows restoring getters", function () { + var object = { + foo: "bar" + }; + + var sandbox = sinonSandbox.create(); + sandbox.stub(object, "foo").get(function () { + return "baz"; + }); + + sandbox.restore(); + + assert.equals(object.foo, "bar"); + }); + + it("allows stubbing setters", function () { + var object = { + prop: "bar" + }; + + var sandbox = sinonSandbox.create(); + sandbox.stub(object, "foo").set(function (val) { + object.prop = val + "bla"; + }); + + object.foo = "bla"; + + assert.equals(object.prop, "blabla"); + }); + + it("allows restoring setters", function () { + var object = { + prop: "bar" + }; + + var sandbox = sinonSandbox.create(); + sandbox.stub(object, "prop").set(function setterFn(val) { + object.prop = val + "bla"; + }); + + sandbox.restore(); + + object.prop = "bla"; + + assert.equals(object.prop, "bla"); + }); + }); }); diff --git a/test/stub-test.js b/test/stub-test.js index 9e4bfc657..01d26f02a 100644 --- a/test/stub-test.js +++ b/test/stub-test.js @@ -2524,4 +2524,26 @@ describe("stub", function () { assert.equals(myObj.otherProp, "bar"); }); }); + + describe(".value", function () { + it("allows stubbing property descriptor values", function () { + var myObj = { + prop: "rawString" + }; + + createStub(myObj, "prop").value("newString"); + assert.equals(myObj.prop, "newString"); + }); + + it("allows restoring stubbed property descriptor values", function () { + var myObj = { + prop: "rawString" + }; + + var stub = createStub(myObj, "prop").value("newString"); + stub.restore(); + + assert.equals(myObj.prop, "rawString"); + }); + }); }); From 0ea3e17ccaa3b00c02a5ca1ee501222d0df32a49 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Sun, 21 May 2017 14:10:41 -0300 Subject: [PATCH 3/5] Add docs for stub.value --- docs/release-source/release/stubs.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/release-source/release/stubs.md b/docs/release-source/release/stubs.md index cd7687749..eddccacff 100644 --- a/docs/release-source/release/stubs.md +++ b/docs/release-source/release/stubs.md @@ -520,3 +520,31 @@ myObj.prop = 'baz'; myObj.example; // 'baz' ``` + +#### `stub.value(newVal)` + +Defines a new value for this stub. + +```javascript +var myObj = { + example: 'oldValue', +}; + +sinon.stub(myObj, 'example').value('newValue'); + +myObj.example; // 'newValue' +``` + +You can restore values by calling the `restore` method: + +```javascript +var myObj = { + example: 'oldValue', +}; + +var stub = sinon.stub(myObj, 'example').value('newValue'); +stub.restore() + +myObj.example; // 'oldValue' +``` + From 454e0fdb178dccb514f2ccf6b306fe6c44ec6b1d Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Sun, 21 May 2017 14:11:43 -0300 Subject: [PATCH 4/5] Add missing final dot to sandbox docs --- docs/release-source/release/sandbox.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-source/release/sandbox.md b/docs/release-source/release/sandbox.md index ecd23639e..da194513f 100644 --- a/docs/release-source/release/sandbox.md +++ b/docs/release-source/release/sandbox.md @@ -106,7 +106,7 @@ Works exactly like `sinon.spy`, only also adds the returned spy to the internal Works almost exactly like `sinon.stub`, only also adds the returned stub to the internal collection of fakes for easy restoring through `sandbox.restore()`. -The sandbox `stub` method can also be used to stub any kind of property. This is useful if you need to override an object's property for the duration of a test, and have it restored when the test completes +The sandbox `stub` method can also be used to stub any kind of property. This is useful if you need to override an object's property for the duration of a test, and have it restored when the test completes. #### `sandbox.mock();` From 46fd81bd43212ed47ea5133e781cf621aa11e1b6 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Sun, 21 May 2017 14:20:39 -0300 Subject: [PATCH 5/5] Remove deprecation warning from tests for old API --- test/collection-test.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/collection-test.js b/test/collection-test.js index 1470f533c..8fd3d0b59 100644 --- a/test/collection-test.js +++ b/test/collection-test.js @@ -5,6 +5,7 @@ var sinonCollection = require("../lib/sinon/collection"); var sinonSpy = require("../lib/sinon/spy"); var sinonStub = require("../lib/sinon/stub"); var assert = referee.assert; +var deprecated = require("../lib/sinon/util/core/deprecated"); describe("collection", function () { it("creates fake collection", function () { @@ -106,8 +107,13 @@ describe("collection", function () { }); it("stubs environment property", function () { + var originalPrintWarning = deprecated.printWarning; + deprecated.printWarning = function () {}; + this.collection.stub(process.env, "HELL", "froze over"); assert.equals(process.env.HELL, "froze over"); + + deprecated.printWarning = originalPrintWarning; }); }); } @@ -120,25 +126,40 @@ describe("collection", function () { }); it("stubs number property", function () { + var originalPrintWarning = deprecated.printWarning; + deprecated.printWarning = function () {}; + this.collection.stub(this.object, "property", 1); assert.equals(this.object.property, 1); + + deprecated.printWarning = originalPrintWarning; }); it("restores number property", function () { + var originalPrintWarning = deprecated.printWarning; + deprecated.printWarning = function () {}; + this.collection.stub(this.object, "property", 1); this.collection.restore(); assert.equals(this.object.property, 42); + + deprecated.printWarning = originalPrintWarning; }); it("fails if property does not exist", function () { + var originalPrintWarning = deprecated.printWarning; + deprecated.printWarning = function () {}; + var collection = this.collection; var object = {}; assert.exception(function () { collection.stub(object, "prop", 1); }); + + deprecated.printWarning = originalPrintWarning; }); it("fails if Symbol does not exist", function () { @@ -146,11 +167,16 @@ describe("collection", function () { var collection = this.collection; var object = {}; + var originalPrintWarning = deprecated.printWarning; + deprecated.printWarning = function () {}; + assert.exception(function () { collection.stub(object, Symbol(), 1); }, function (err) { return err.message === "Cannot stub non-existent own property Symbol()"; }); + + deprecated.printWarning = originalPrintWarning; } }); });