diff --git a/docs/release-source/release/stubs.md b/docs/release-source/release/stubs.md index b272f8e94..214b05347 100644 --- a/docs/release-source/release/stubs.md +++ b/docs/release-source/release/stubs.md @@ -437,16 +437,14 @@ Same as their corresponding non-Async counterparts, but with callback being defe #### `sinon.addBehavior(name, fn);` -Add a custom behavior. The name will be available as a function on stubs, and the chaining mechanism will be set up for you. Inside the `fn`, `this` is bound to the fake. +Add a custom behavior. The name will be available as a function on stubs, and the chaining mechanism will be set up for you (e.g. no need to return anything from your function, it's return value will be ignored). The `fn` will be passed the fake instance as its first argument, and then the user's arguments. ```javascript const sinon = require('sinon'); -sinon.addBehavior('returns42', function () { - this.returns(42); -}); +sinon.addBehavior('returnsNum', (fake, n) => fake.returns(n)); -var stub = sinon.stub().returns42(); +var stub = sinon.stub().returnsNum(42); assert.equals(stub(), 42); ``` diff --git a/lib/sinon/behavior.js b/lib/sinon/behavior.js index 3a9af44a5..42112d778 100644 --- a/lib/sinon/behavior.js +++ b/lib/sinon/behavior.js @@ -197,7 +197,7 @@ function createBehavior(behaviorMethod) { function addBehavior(stub, name, fn) { proto[name] = function () { - fn.apply(this, arguments); + fn.apply(this, [this].concat([].slice.call(arguments))); return this.stub || this; }; diff --git a/lib/sinon/default-behaviors.js b/lib/sinon/default-behaviors.js index 5b3c4c5b3..263c500ca 100644 --- a/lib/sinon/default-behaviors.js +++ b/lib/sinon/default-behaviors.js @@ -4,144 +4,144 @@ var slice = [].slice; var useLeftMostCallback = -1; var useRightMostCallback = -2; -function throwsException(error, message) { +function throwsException(fake, error, message) { if (typeof error === "string") { - this.exception = new Error(message || ""); - this.exception.name = error; + fake.exception = new Error(message || ""); + fake.exception.name = error; } else if (!error) { - this.exception = new Error("Error"); + fake.exception = new Error("Error"); } else { - this.exception = error; + fake.exception = error; } } module.exports = { - callsFake: function callsFake(fn) { - this.fakeFn = fn; + callsFake: function callsFake(fake, fn) { + fake.fakeFn = fn; }, - callsArg: function callsArg(pos) { + callsArg: function callsArg(fake, pos) { if (typeof pos !== "number") { throw new TypeError("argument index is not number"); } - this.callArgAt = pos; - this.callbackArguments = []; - this.callbackContext = undefined; - this.callArgProp = undefined; - this.callbackAsync = false; + fake.callArgAt = pos; + fake.callbackArguments = []; + fake.callbackContext = undefined; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - callsArgOn: function callsArgOn(pos, context) { + callsArgOn: function callsArgOn(fake, pos, context) { if (typeof pos !== "number") { throw new TypeError("argument index is not number"); } - this.callArgAt = pos; - this.callbackArguments = []; - this.callbackContext = context; - this.callArgProp = undefined; - this.callbackAsync = false; + fake.callArgAt = pos; + fake.callbackArguments = []; + fake.callbackContext = context; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - callsArgWith: function callsArgWith(pos) { + callsArgWith: function callsArgWith(fake, pos) { if (typeof pos !== "number") { throw new TypeError("argument index is not number"); } - this.callArgAt = pos; - this.callbackArguments = slice.call(arguments, 1); - this.callbackContext = undefined; - this.callArgProp = undefined; - this.callbackAsync = false; + fake.callArgAt = pos; + fake.callbackArguments = slice.call(arguments, 2); + fake.callbackContext = undefined; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - callsArgOnWith: function callsArgWith(pos, context) { + callsArgOnWith: function callsArgWith(fake, pos, context) { if (typeof pos !== "number") { throw new TypeError("argument index is not number"); } - this.callArgAt = pos; - this.callbackArguments = slice.call(arguments, 2); - this.callbackContext = context; - this.callArgProp = undefined; - this.callbackAsync = false; + fake.callArgAt = pos; + fake.callbackArguments = slice.call(arguments, 3); + fake.callbackContext = context; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - yields: function () { - this.callArgAt = useLeftMostCallback; - this.callbackArguments = slice.call(arguments, 0); - this.callbackContext = undefined; - this.callArgProp = undefined; - this.callbackAsync = false; + yields: function (fake) { + fake.callArgAt = useLeftMostCallback; + fake.callbackArguments = slice.call(arguments, 1); + fake.callbackContext = undefined; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - yieldsRight: function () { - this.callArgAt = useRightMostCallback; - this.callbackArguments = slice.call(arguments, 0); - this.callbackContext = undefined; - this.callArgProp = undefined; - this.callbackAsync = false; + yieldsRight: function (fake) { + fake.callArgAt = useRightMostCallback; + fake.callbackArguments = slice.call(arguments, 1); + fake.callbackContext = undefined; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - yieldsOn: function (context) { - this.callArgAt = useLeftMostCallback; - this.callbackArguments = slice.call(arguments, 1); - this.callbackContext = context; - this.callArgProp = undefined; - this.callbackAsync = false; + yieldsOn: function (fake, context) { + fake.callArgAt = useLeftMostCallback; + fake.callbackArguments = slice.call(arguments, 2); + fake.callbackContext = context; + fake.callArgProp = undefined; + fake.callbackAsync = false; }, - yieldsTo: function (prop) { - this.callArgAt = useLeftMostCallback; - this.callbackArguments = slice.call(arguments, 1); - this.callbackContext = undefined; - this.callArgProp = prop; - this.callbackAsync = false; + yieldsTo: function (fake, prop) { + fake.callArgAt = useLeftMostCallback; + fake.callbackArguments = slice.call(arguments, 2); + fake.callbackContext = undefined; + fake.callArgProp = prop; + fake.callbackAsync = false; }, - yieldsToOn: function (prop, context) { - this.callArgAt = useLeftMostCallback; - this.callbackArguments = slice.call(arguments, 2); - this.callbackContext = context; - this.callArgProp = prop; - this.callbackAsync = false; + yieldsToOn: function (fake, prop, context) { + fake.callArgAt = useLeftMostCallback; + fake.callbackArguments = slice.call(arguments, 3); + fake.callbackContext = context; + fake.callArgProp = prop; + fake.callbackAsync = false; }, throws: throwsException, throwsException: throwsException, - returns: function returns(value) { - this.returnValue = value; - this.resolve = false; - this.reject = false; - this.returnValueDefined = true; - this.exception = undefined; - this.fakeFn = undefined; + returns: function returns(fake, value) { + fake.returnValue = value; + fake.resolve = false; + fake.reject = false; + fake.returnValueDefined = true; + fake.exception = undefined; + fake.fakeFn = undefined; }, - returnsArg: function returnsArg(pos) { + returnsArg: function returnsArg(fake, pos) { if (typeof pos !== "number") { throw new TypeError("argument index is not number"); } - this.returnArgAt = pos; + fake.returnArgAt = pos; }, - returnsThis: function returnsThis() { - this.returnThis = true; + returnsThis: function returnsThis(fake) { + fake.returnThis = true; }, - resolves: function resolves(value) { - this.returnValue = value; - this.resolve = true; - this.reject = false; - this.returnValueDefined = true; - this.exception = undefined; - this.fakeFn = undefined; + resolves: function resolves(fake, value) { + fake.returnValue = value; + fake.resolve = true; + fake.reject = false; + fake.returnValueDefined = true; + fake.exception = undefined; + fake.fakeFn = undefined; }, - rejects: function rejects(error, message) { + rejects: function rejects(fake, error, message) { var reason; if (typeof error === "string") { reason = new Error(message || ""); @@ -151,24 +151,24 @@ module.exports = { } else { reason = error; } - this.returnValue = reason; - this.resolve = false; - this.reject = true; - this.returnValueDefined = true; - this.exception = undefined; - this.fakeFn = undefined; - - return this; + fake.returnValue = reason; + fake.resolve = false; + fake.reject = true; + fake.returnValueDefined = true; + fake.exception = undefined; + fake.fakeFn = undefined; + + return fake; }, - callThrough: function callThrough() { - this.callsThrough = true; + callThrough: function callThrough(fake) { + fake.callsThrough = true; } }; function createAsyncVersion(syncFnName) { return function () { - var result = this[syncFnName].apply(this, arguments); + var result = module.exports[syncFnName].apply(this, arguments); this.callbackAsync = true; return result; }; diff --git a/test/behavior-test.js b/test/behavior-test.js index ee00503c5..c92719c74 100644 --- a/test/behavior-test.js +++ b/test/behavior-test.js @@ -6,11 +6,11 @@ var assert = require("referee").assert; describe("behaviors", function () { it("adds and uses a custom behavior", function () { - addBehavior("returns42", function () { - this.returns(42); + addBehavior("returnsNum", function (fake, n) { + fake.returns(n); }); - var stub = createStub().returns42(); + var stub = createStub().returnsNum(42); assert.equals(stub(), 42); });