Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getters/setters stub behaviors new API #1297

Merged
merged 5 commits into from
Mar 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/release-source/release/stubs.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,38 @@ var stub = sinon.stub().returnsNum(42);

assert.equals(stub(), 42);
```

#### `stub.get(getterFn)`

Replaces a new getter for this stub.

```javascript
var myObj = {
prop: 'foo'
};

createStub(myObj, 'prop').get(function getterFn() {
return 'bar';
});

myObj.example; // 'bar'
```

#### `stub.set(setterFn)`

Defines a new setter for this stub.

```javascript
var myObj = {
example: 'oldValue',
prop: 'foo'
};

createStub(myObj, 'prop').set(function setterFn(val) {
myObj.example = val;
});

myObj.prop = 'baz';

myObj.example; // 'baz'
```
20 changes: 20 additions & 0 deletions lib/sinon/default-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,26 @@ module.exports = {

callThrough: function callThrough(fake) {
fake.callsThrough = true;
},

get: function get(fake, getterFunction) {
var rootStub = fake.stub || fake;

Object.defineProperty(rootStub.rootObj, rootStub.propName, {
get: getterFunction
});

return fake;
},

set: function set(fake, setterFunction) {
var rootStub = fake.stub || fake;

Object.defineProperty(rootStub.rootObj, rootStub.propName, { // eslint-disable-line accessor-pairs
set: setterFunction
});

return fake;
}
};

Expand Down
25 changes: 20 additions & 5 deletions lib/sinon/stub.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var behaviors = require("./default-behaviors");
var spy = require("./spy");
var extend = require("./util/core/extend");
var functionToString = require("./util/core/function-to-string");
var getPropertyDescriptor = require("./util/core/get-property-descriptor");
var wrapMethod = require("./util/core/wrap-method");
var stubEntireObject = require("./stub-entire-object");
var stubDescriptor = require("./stub-descriptor");
Expand All @@ -13,27 +14,41 @@ var throwOnFalsyObject = require("./throw-on-falsy-object");
function stub(object, property, descriptor) {
throwOnFalsyObject.apply(null, arguments);

var actualDescriptor = getPropertyDescriptor(object, property);
var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
var isCreatingNewStub = !object && typeof property === "undefined";
var isStubbingDescriptor = object && property && Boolean(descriptor);
var isStubbingNonFuncProperty = typeof object === "object"
&& typeof property !== "undefined"
&& (typeof actualDescriptor === "undefined"
|| typeof actualDescriptor.value !== "function")
&& typeof descriptor === "undefined";
var isStubbingExistingMethod = !isStubbingDescriptor
&& typeof object === "object"
&& typeof object[property] === "function";
&& typeof actualDescriptor !== "undefined"
&& typeof actualDescriptor.value === "function";
var arity = isStubbingExistingMethod ? object[property].length : 0;

if (isStubbingEntireObject) {
return stubEntireObject(stub, object);
}

if (isStubbingDescriptor) {
return stubDescriptor.apply(null, arguments);
}

if (isCreatingNewStub) {
return stub.create();
}

if (isStubbingDescriptor) {
return stubDescriptor.apply(null, arguments);
}
var s = stub.create(arity);
s.rootObj = object;
s.propName = property;
s.restore = function restore() {
Object.defineProperty(object, property, actualDescriptor);
};

return wrapMethod(object, property, stub.create(arity));
return isStubbingNonFuncProperty ? s : wrapMethod(object, property, s);
}

stub.createStubInstance = function (constructor) {
Expand Down
210 changes: 190 additions & 20 deletions test/stub-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -800,16 +800,6 @@ describe("stub", function () {
assert.isFalse(stub.called);
});

it("throws if property is not a function", function () {
var obj = { someProp: 42 };

assert.exception(function () {
createStub(obj, "someProp");
});

assert.equals(obj.someProp, 42);
});

it("successfully stubs falsey properties", function () {
var obj = { 0: function () { } };

Expand Down Expand Up @@ -943,16 +933,6 @@ describe("stub", function () {
});

describe("stubbed function", function () {
it("throws if stubbing non-existent property", function () {
var myObj = {};

assert.exception(function () {
createStub(myObj, "ouch");
});

refute.defined(myObj.ouch);
});

it("has toString method", function () {
var obj = { meth: function () {} };
createStub(obj, "meth");
Expand Down Expand Up @@ -2301,4 +2281,194 @@ describe("stub", function () {
assert.equals(reference, myObj);
});
});

describe(".get", function () {
it("allows users to stub getter functions for properties", function () {
var myObj = {
prop: "foo"
};

createStub(myObj, "prop").get(function getterFn() {
return "bar";
});

assert.equals(myObj.prop, "bar");
});

it("allows users to stub getter functions for functions", function () {
var myObj = {
prop: function propGetter() {
return "foo";
}
};

createStub(myObj, "prop").get(function getterFn() {
return "bar";
});

assert.equals(myObj.prop, "bar");
});

it("replaces old getters", function () {
var myObj = {
get prop() {
fail("should not call the old getter");
}
};

createStub(myObj, "prop").get(function getterFn() {
return "bar";
});

assert.equals(myObj.prop, "bar");
});

it("can set getters for non-existing properties", function () {
var myObj = {};

createStub(myObj, "prop").get(function getterFn() {
return "bar";
});

assert.equals(myObj.prop, "bar");
});

it("can restore stubbed setters for functions", function () {
var propFn = function propFn() {
return "bar";
};

var myObj = {
prop: propFn
};

var stub = createStub(myObj, "prop");

stub.get(function getterFn() {
return "baz";
});

stub.restore();

assert.equals(myObj.prop, propFn);
});

it("can restore stubbed getters for properties", function () {
var myObj = {
get prop() {
return "bar";
}
};

var stub = createStub(myObj, "prop");

stub.get(function getterFn() {
return "baz";
});

stub.restore();

assert.equals(myObj.prop, "bar");
});
});

describe(".set", function () {
it("allows users to stub setter functions for properties", function () {
var myObj = {
prop: "foo"
};

createStub(myObj, "prop").set(function setterFn() {
myObj.example = "bar";
});

myObj.prop = "baz";

assert.equals(myObj.example, "bar");
});

it("allows users to stub setter functions for functions", function () {
var myObj = {
prop: function propSetter() {
return "foo";
}
};

createStub(myObj, "prop").set(function setterFn() {
myObj.example = "bar";
});

myObj.prop = "baz";

assert.equals(myObj.example, "bar");
});

it("replaces old setters", function () {
var myObj = { // eslint-disable-line accessor-pairs
set prop(val) {
fail("should not call the old setter");
}
};

createStub(myObj, "prop").set(function setterFn() {
myObj.example = "bar";
});

myObj.prop = "foo";

assert.equals(myObj.example, "bar");
});

it("can set setters for non-existing properties", function () {
var myObj = {};

createStub(myObj, "prop").set(function setterFn() {
myObj.example = "bar";
});

myObj.prop = "foo";

assert.equals(myObj.example, "bar");
});

it("can restore stubbed setters for functions", function () {
var propFn = function propFn() {
return "bar";
};

var myObj = {
prop: propFn
};

var stub = createStub(myObj, "prop");

stub.set(function setterFn() {
myObj.otherProp = "baz";
});

stub.restore();

assert.equals(myObj.prop, propFn);
});

it("can restore stubbed setters for properties", function () {
var myObj = { // eslint-disable-line accessor-pairs
set prop(val) {
this.otherProp = "bar";
return "bar";
}
};

var stub = createStub(myObj, "prop");

stub.set(function setterFn() {
myObj.otherProp = "baz";
});

stub.restore();

myObj.prop = "foo";
assert.equals(myObj.otherProp, "bar");
});
});
});