Skip to content

Commit fee959d

Browse files
authored
Merge pull request #1297 from lucasfcosta/getters-setters-new-api
Add getters/setters stub behaviors new API
2 parents 1bda7c4 + ad58467 commit fee959d

4 files changed

Lines changed: 265 additions & 25 deletions

File tree

docs/release-source/release/stubs.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,3 +448,38 @@ var stub = sinon.stub().returnsNum(42);
448448

449449
assert.equals(stub(), 42);
450450
```
451+
452+
#### `stub.get(getterFn)`
453+
454+
Replaces a new getter for this stub.
455+
456+
```javascript
457+
var myObj = {
458+
prop: 'foo'
459+
};
460+
461+
createStub(myObj, 'prop').get(function getterFn() {
462+
return 'bar';
463+
});
464+
465+
myObj.example; // 'bar'
466+
```
467+
468+
#### `stub.set(setterFn)`
469+
470+
Defines a new setter for this stub.
471+
472+
```javascript
473+
var myObj = {
474+
example: 'oldValue',
475+
prop: 'foo'
476+
};
477+
478+
createStub(myObj, 'prop').set(function setterFn(val) {
479+
myObj.example = val;
480+
});
481+
482+
myObj.prop = 'baz';
483+
484+
myObj.example; // 'baz'
485+
```

lib/sinon/default-behaviors.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,26 @@ module.exports = {
163163

164164
callThrough: function callThrough(fake) {
165165
fake.callsThrough = true;
166+
},
167+
168+
get: function get(fake, getterFunction) {
169+
var rootStub = fake.stub || fake;
170+
171+
Object.defineProperty(rootStub.rootObj, rootStub.propName, {
172+
get: getterFunction
173+
});
174+
175+
return fake;
176+
},
177+
178+
set: function set(fake, setterFunction) {
179+
var rootStub = fake.stub || fake;
180+
181+
Object.defineProperty(rootStub.rootObj, rootStub.propName, { // eslint-disable-line accessor-pairs
182+
set: setterFunction
183+
});
184+
185+
return fake;
166186
}
167187
};
168188

lib/sinon/stub.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var behaviors = require("./default-behaviors");
55
var spy = require("./spy");
66
var extend = require("./util/core/extend");
77
var functionToString = require("./util/core/function-to-string");
8+
var getPropertyDescriptor = require("./util/core/get-property-descriptor");
89
var wrapMethod = require("./util/core/wrap-method");
910
var stubEntireObject = require("./stub-entire-object");
1011
var stubDescriptor = require("./stub-descriptor");
@@ -13,27 +14,41 @@ var throwOnFalsyObject = require("./throw-on-falsy-object");
1314
function stub(object, property, descriptor) {
1415
throwOnFalsyObject.apply(null, arguments);
1516

17+
var actualDescriptor = getPropertyDescriptor(object, property);
1618
var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
1719
var isCreatingNewStub = !object && typeof property === "undefined";
1820
var isStubbingDescriptor = object && property && Boolean(descriptor);
21+
var isStubbingNonFuncProperty = typeof object === "object"
22+
&& typeof property !== "undefined"
23+
&& (typeof actualDescriptor === "undefined"
24+
|| typeof actualDescriptor.value !== "function")
25+
&& typeof descriptor === "undefined";
1926
var isStubbingExistingMethod = !isStubbingDescriptor
2027
&& typeof object === "object"
21-
&& typeof object[property] === "function";
28+
&& typeof actualDescriptor !== "undefined"
29+
&& typeof actualDescriptor.value === "function";
2230
var arity = isStubbingExistingMethod ? object[property].length : 0;
2331

2432
if (isStubbingEntireObject) {
2533
return stubEntireObject(stub, object);
2634
}
2735

36+
if (isStubbingDescriptor) {
37+
return stubDescriptor.apply(null, arguments);
38+
}
39+
2840
if (isCreatingNewStub) {
2941
return stub.create();
3042
}
3143

32-
if (isStubbingDescriptor) {
33-
return stubDescriptor.apply(null, arguments);
34-
}
44+
var s = stub.create(arity);
45+
s.rootObj = object;
46+
s.propName = property;
47+
s.restore = function restore() {
48+
Object.defineProperty(object, property, actualDescriptor);
49+
};
3550

36-
return wrapMethod(object, property, stub.create(arity));
51+
return isStubbingNonFuncProperty ? s : wrapMethod(object, property, s);
3752
}
3853

3954
stub.createStubInstance = function (constructor) {

test/stub-test.js

Lines changed: 190 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -800,16 +800,6 @@ describe("stub", function () {
800800
assert.isFalse(stub.called);
801801
});
802802

803-
it("throws if property is not a function", function () {
804-
var obj = { someProp: 42 };
805-
806-
assert.exception(function () {
807-
createStub(obj, "someProp");
808-
});
809-
810-
assert.equals(obj.someProp, 42);
811-
});
812-
813803
it("successfully stubs falsey properties", function () {
814804
var obj = { 0: function () { } };
815805

@@ -943,16 +933,6 @@ describe("stub", function () {
943933
});
944934

945935
describe("stubbed function", function () {
946-
it("throws if stubbing non-existent property", function () {
947-
var myObj = {};
948-
949-
assert.exception(function () {
950-
createStub(myObj, "ouch");
951-
});
952-
953-
refute.defined(myObj.ouch);
954-
});
955-
956936
it("has toString method", function () {
957937
var obj = { meth: function () {} };
958938
createStub(obj, "meth");
@@ -2301,4 +2281,194 @@ describe("stub", function () {
23012281
assert.equals(reference, myObj);
23022282
});
23032283
});
2284+
2285+
describe(".get", function () {
2286+
it("allows users to stub getter functions for properties", function () {
2287+
var myObj = {
2288+
prop: "foo"
2289+
};
2290+
2291+
createStub(myObj, "prop").get(function getterFn() {
2292+
return "bar";
2293+
});
2294+
2295+
assert.equals(myObj.prop, "bar");
2296+
});
2297+
2298+
it("allows users to stub getter functions for functions", function () {
2299+
var myObj = {
2300+
prop: function propGetter() {
2301+
return "foo";
2302+
}
2303+
};
2304+
2305+
createStub(myObj, "prop").get(function getterFn() {
2306+
return "bar";
2307+
});
2308+
2309+
assert.equals(myObj.prop, "bar");
2310+
});
2311+
2312+
it("replaces old getters", function () {
2313+
var myObj = {
2314+
get prop() {
2315+
fail("should not call the old getter");
2316+
}
2317+
};
2318+
2319+
createStub(myObj, "prop").get(function getterFn() {
2320+
return "bar";
2321+
});
2322+
2323+
assert.equals(myObj.prop, "bar");
2324+
});
2325+
2326+
it("can set getters for non-existing properties", function () {
2327+
var myObj = {};
2328+
2329+
createStub(myObj, "prop").get(function getterFn() {
2330+
return "bar";
2331+
});
2332+
2333+
assert.equals(myObj.prop, "bar");
2334+
});
2335+
2336+
it("can restore stubbed setters for functions", function () {
2337+
var propFn = function propFn() {
2338+
return "bar";
2339+
};
2340+
2341+
var myObj = {
2342+
prop: propFn
2343+
};
2344+
2345+
var stub = createStub(myObj, "prop");
2346+
2347+
stub.get(function getterFn() {
2348+
return "baz";
2349+
});
2350+
2351+
stub.restore();
2352+
2353+
assert.equals(myObj.prop, propFn);
2354+
});
2355+
2356+
it("can restore stubbed getters for properties", function () {
2357+
var myObj = {
2358+
get prop() {
2359+
return "bar";
2360+
}
2361+
};
2362+
2363+
var stub = createStub(myObj, "prop");
2364+
2365+
stub.get(function getterFn() {
2366+
return "baz";
2367+
});
2368+
2369+
stub.restore();
2370+
2371+
assert.equals(myObj.prop, "bar");
2372+
});
2373+
});
2374+
2375+
describe(".set", function () {
2376+
it("allows users to stub setter functions for properties", function () {
2377+
var myObj = {
2378+
prop: "foo"
2379+
};
2380+
2381+
createStub(myObj, "prop").set(function setterFn() {
2382+
myObj.example = "bar";
2383+
});
2384+
2385+
myObj.prop = "baz";
2386+
2387+
assert.equals(myObj.example, "bar");
2388+
});
2389+
2390+
it("allows users to stub setter functions for functions", function () {
2391+
var myObj = {
2392+
prop: function propSetter() {
2393+
return "foo";
2394+
}
2395+
};
2396+
2397+
createStub(myObj, "prop").set(function setterFn() {
2398+
myObj.example = "bar";
2399+
});
2400+
2401+
myObj.prop = "baz";
2402+
2403+
assert.equals(myObj.example, "bar");
2404+
});
2405+
2406+
it("replaces old setters", function () {
2407+
var myObj = { // eslint-disable-line accessor-pairs
2408+
set prop(val) {
2409+
fail("should not call the old setter");
2410+
}
2411+
};
2412+
2413+
createStub(myObj, "prop").set(function setterFn() {
2414+
myObj.example = "bar";
2415+
});
2416+
2417+
myObj.prop = "foo";
2418+
2419+
assert.equals(myObj.example, "bar");
2420+
});
2421+
2422+
it("can set setters for non-existing properties", function () {
2423+
var myObj = {};
2424+
2425+
createStub(myObj, "prop").set(function setterFn() {
2426+
myObj.example = "bar";
2427+
});
2428+
2429+
myObj.prop = "foo";
2430+
2431+
assert.equals(myObj.example, "bar");
2432+
});
2433+
2434+
it("can restore stubbed setters for functions", function () {
2435+
var propFn = function propFn() {
2436+
return "bar";
2437+
};
2438+
2439+
var myObj = {
2440+
prop: propFn
2441+
};
2442+
2443+
var stub = createStub(myObj, "prop");
2444+
2445+
stub.set(function setterFn() {
2446+
myObj.otherProp = "baz";
2447+
});
2448+
2449+
stub.restore();
2450+
2451+
assert.equals(myObj.prop, propFn);
2452+
});
2453+
2454+
it("can restore stubbed setters for properties", function () {
2455+
var myObj = { // eslint-disable-line accessor-pairs
2456+
set prop(val) {
2457+
this.otherProp = "bar";
2458+
return "bar";
2459+
}
2460+
};
2461+
2462+
var stub = createStub(myObj, "prop");
2463+
2464+
stub.set(function setterFn() {
2465+
myObj.otherProp = "baz";
2466+
});
2467+
2468+
stub.restore();
2469+
2470+
myObj.prop = "foo";
2471+
assert.equals(myObj.otherProp, "bar");
2472+
});
2473+
});
23042474
});

0 commit comments

Comments
 (0)