Skip to content

Commit 55dc491

Browse files
authored
Merge pull request #1416 from lucasfcosta/refactor-sandbox-stub
Refactor sandbox and allow it to stub getters and setters
2 parents fb514cf + 46fd81b commit 55dc491

File tree

9 files changed

+215
-9
lines changed

9 files changed

+215
-9
lines changed

docs/release-source/release/sandbox.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Works exactly like `sinon.spy`, only also adds the returned spy to the internal
106106

107107
Works almost exactly like `sinon.stub`, only also adds the returned stub to the internal collection of fakes for easy restoring through `sandbox.restore()`.
108108

109-
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
109+
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.
110110

111111
#### `sandbox.mock();`
112112

docs/release-source/release/stubs.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,31 @@ myObj.prop = 'baz';
520520

521521
myObj.example; // 'baz'
522522
```
523+
524+
#### `stub.value(newVal)`
525+
526+
Defines a new value for this stub.
527+
528+
```javascript
529+
var myObj = {
530+
example: 'oldValue',
531+
};
532+
533+
sinon.stub(myObj, 'example').value('newValue');
534+
535+
myObj.example; // 'newValue'
536+
```
537+
538+
You can restore values by calling the `restore` method:
539+
540+
```javascript
541+
var myObj = {
542+
example: 'oldValue',
543+
};
544+
545+
var stub = sinon.stub(myObj, 'example').value('newValue');
546+
stub.restore()
547+
548+
myObj.example; // 'oldValue'
549+
```
550+

lib/sinon/collection.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
var sinonSpy = require("./spy");
44
var sinonStub = require("./stub");
55
var sinonMock = require("./mock");
6-
var throwOnFalsyObject = require("./throw-on-falsy-object");
6+
var sandboxStub = require("./sandbox-stub");
77
var collectOwnMethods = require("./collect-own-methods");
8-
var stubNonFunctionProperty = require("./stub-non-function-property");
98

109
var push = [].push;
1110

@@ -81,13 +80,12 @@ var collection = {
8180
},
8281

8382
stub: function stub(object, property/*, value*/) {
84-
throwOnFalsyObject.apply(null, arguments);
83+
if (arguments.length > 2) {
84+
return sandboxStub.apply(this, arguments);
85+
}
8586

87+
var stubbed = sinonStub.apply(null, arguments);
8688
var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
87-
var isStubbingNonFunctionProperty = property && typeof object[property] !== "function";
88-
var stubbed = isStubbingNonFunctionProperty ?
89-
stubNonFunctionProperty.apply(null, arguments) :
90-
sinonStub.apply(null, arguments);
9189

9290
if (isStubbingEntireObject) {
9391
var ownMethods = collectOwnMethods(stubbed);

lib/sinon/default-behaviors.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"use strict";
2+
var getPropertyDescriptor = require("./util/core/get-property-descriptor");
23

34
var slice = [].slice;
45
var useLeftMostCallback = -1;
@@ -194,6 +195,24 @@ module.exports = {
194195
set: setterFunction
195196
});
196197

198+
return fake;
199+
},
200+
201+
value: function value(fake, newVal) {
202+
var rootStub = fake.stub || fake;
203+
204+
var oldVal = getPropertyDescriptor(rootStub.rootObj, rootStub.propName).value;
205+
206+
Object.defineProperty(rootStub.rootObj, rootStub.propName, {
207+
value: newVal
208+
});
209+
210+
fake.restore = function restore() {
211+
Object.defineProperty(rootStub.rootObj, rootStub.propName, {
212+
value: oldVal
213+
});
214+
};
215+
197216
return fake;
198217
}
199218
};

lib/sinon/sandbox-stub.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use strict";
2+
3+
var collectOwnMethods = require("./collect-own-methods");
4+
var deprecated = require("./util/core/deprecated");
5+
var getPropertyDescriptor = require("./util/core/get-property-descriptor");
6+
var stubNonFunctionProperty = require("./stub-non-function-property");
7+
var sinonStub = require("./stub");
8+
var throwOnFalsyObject = require("./throw-on-falsy-object");
9+
10+
// This is deprecated and will be removed in a future version of sinon.
11+
// We will only consider pull requests that fix serious bugs in the implementation
12+
function sandboxStub(object, property/*, value*/) {
13+
deprecated.printWarning(
14+
"sandbox.stub(obj, 'meth', val) is deprecated and will be removed from " +
15+
"the public API in a future version of sinon." +
16+
"\n Use sandbox(obj, 'meth').callsFake(fn) instead in order to stub a function." +
17+
"\n Use sandbox(obj, 'meth').value(fn) instead in order to stub a non-function value."
18+
);
19+
20+
throwOnFalsyObject.apply(null, arguments);
21+
22+
var actualDescriptor = getPropertyDescriptor(object, property);
23+
var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
24+
var isStubbingNonFuncProperty = typeof object === "object"
25+
&& typeof property !== "undefined"
26+
&& (typeof actualDescriptor === "undefined"
27+
|| typeof actualDescriptor.value !== "function");
28+
29+
30+
// When passing a value as third argument it will be applied to stubNonFunctionProperty
31+
var stubbed = isStubbingNonFuncProperty ?
32+
stubNonFunctionProperty.apply(null, arguments) :
33+
sinonStub.apply(null, arguments);
34+
35+
if (isStubbingEntireObject) {
36+
var ownMethods = collectOwnMethods(stubbed);
37+
ownMethods.forEach(this.add.bind(this));
38+
if (this.promiseLibrary) {
39+
ownMethods.forEach(this.addUsingPromise.bind(this));
40+
}
41+
} else {
42+
this.add(stubbed);
43+
if (this.promiseLibrary) {
44+
stubbed.usingPromise(this.promiseLibrary);
45+
}
46+
}
47+
48+
return stubbed;
49+
}
50+
51+
module.exports = sandboxStub;

lib/sinon/stub-non-function-property.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function stubNonFunctionProperty(object, property, value) {
1313
object[property] = value;
1414

1515
return {
16-
restore: function () {
16+
restore: function restore() {
1717
object[property] = original;
1818
}
1919
};

test/collection-test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var sinonCollection = require("../lib/sinon/collection");
55
var sinonSpy = require("../lib/sinon/spy");
66
var sinonStub = require("../lib/sinon/stub");
77
var assert = referee.assert;
8+
var deprecated = require("../lib/sinon/util/core/deprecated");
89

910
describe("collection", function () {
1011
it("creates fake collection", function () {
@@ -106,8 +107,13 @@ describe("collection", function () {
106107
});
107108

108109
it("stubs environment property", function () {
110+
var originalPrintWarning = deprecated.printWarning;
111+
deprecated.printWarning = function () {};
112+
109113
this.collection.stub(process.env, "HELL", "froze over");
110114
assert.equals(process.env.HELL, "froze over");
115+
116+
deprecated.printWarning = originalPrintWarning;
111117
});
112118
});
113119
}
@@ -120,37 +126,57 @@ describe("collection", function () {
120126
});
121127

122128
it("stubs number property", function () {
129+
var originalPrintWarning = deprecated.printWarning;
130+
deprecated.printWarning = function () {};
131+
123132
this.collection.stub(this.object, "property", 1);
124133

125134
assert.equals(this.object.property, 1);
135+
136+
deprecated.printWarning = originalPrintWarning;
126137
});
127138

128139
it("restores number property", function () {
140+
var originalPrintWarning = deprecated.printWarning;
141+
deprecated.printWarning = function () {};
142+
129143
this.collection.stub(this.object, "property", 1);
130144
this.collection.restore();
131145

132146
assert.equals(this.object.property, 42);
147+
148+
deprecated.printWarning = originalPrintWarning;
133149
});
134150

135151
it("fails if property does not exist", function () {
152+
var originalPrintWarning = deprecated.printWarning;
153+
deprecated.printWarning = function () {};
154+
136155
var collection = this.collection;
137156
var object = {};
138157

139158
assert.exception(function () {
140159
collection.stub(object, "prop", 1);
141160
});
161+
162+
deprecated.printWarning = originalPrintWarning;
142163
});
143164

144165
it("fails if Symbol does not exist", function () {
145166
if (typeof Symbol === "function") {
146167
var collection = this.collection;
147168
var object = {};
148169

170+
var originalPrintWarning = deprecated.printWarning;
171+
deprecated.printWarning = function () {};
172+
149173
assert.exception(function () {
150174
collection.stub(object, Symbol(), 1);
151175
}, function (err) {
152176
return err.message === "Cannot stub non-existent own property Symbol()";
153177
});
178+
179+
deprecated.printWarning = originalPrintWarning;
154180
}
155181
});
156182
});

test/sandbox-test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,4 +548,66 @@ describe("sinonSandbox", function () {
548548
sandbox.restore();
549549
});
550550
});
551+
552+
describe("getters and setters", function () {
553+
it("allows stubbing getters", function () {
554+
var object = {
555+
foo: "bar"
556+
};
557+
558+
var sandbox = sinonSandbox.create();
559+
sandbox.stub(object, "foo").get(function () {
560+
return "baz";
561+
});
562+
563+
assert.equals(object.foo, "baz");
564+
});
565+
566+
it("allows restoring getters", function () {
567+
var object = {
568+
foo: "bar"
569+
};
570+
571+
var sandbox = sinonSandbox.create();
572+
sandbox.stub(object, "foo").get(function () {
573+
return "baz";
574+
});
575+
576+
sandbox.restore();
577+
578+
assert.equals(object.foo, "bar");
579+
});
580+
581+
it("allows stubbing setters", function () {
582+
var object = {
583+
prop: "bar"
584+
};
585+
586+
var sandbox = sinonSandbox.create();
587+
sandbox.stub(object, "foo").set(function (val) {
588+
object.prop = val + "bla";
589+
});
590+
591+
object.foo = "bla";
592+
593+
assert.equals(object.prop, "blabla");
594+
});
595+
596+
it("allows restoring setters", function () {
597+
var object = {
598+
prop: "bar"
599+
};
600+
601+
var sandbox = sinonSandbox.create();
602+
sandbox.stub(object, "prop").set(function setterFn(val) {
603+
object.prop = val + "bla";
604+
});
605+
606+
sandbox.restore();
607+
608+
object.prop = "bla";
609+
610+
assert.equals(object.prop, "bla");
611+
});
612+
});
551613
});

test/stub-test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2620,4 +2620,26 @@ describe("stub", function () {
26202620
assert.equals(myObj.otherProp, "bar");
26212621
});
26222622
});
2623+
2624+
describe(".value", function () {
2625+
it("allows stubbing property descriptor values", function () {
2626+
var myObj = {
2627+
prop: "rawString"
2628+
};
2629+
2630+
createStub(myObj, "prop").value("newString");
2631+
assert.equals(myObj.prop, "newString");
2632+
});
2633+
2634+
it("allows restoring stubbed property descriptor values", function () {
2635+
var myObj = {
2636+
prop: "rawString"
2637+
};
2638+
2639+
var stub = createStub(myObj, "prop").value("newString");
2640+
stub.restore();
2641+
2642+
assert.equals(myObj.prop, "rawString");
2643+
});
2644+
});
26232645
});

0 commit comments

Comments
 (0)