From 3c877549ef9803a441900f06e81024d7793138b1 Mon Sep 17 00:00:00 2001 From: George Schneeloch Date: Sun, 6 Aug 2017 10:18:38 -0400 Subject: [PATCH] Add support for passing a function to stub.throws(...). (#1511) --- docs/release-source/release/stubs.md | 5 ++ lib/sinon/behavior.js | 4 ++ lib/sinon/default-behaviors.js | 18 ++++-- test/stub-test.js | 85 ++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/docs/release-source/release/stubs.md b/docs/release-source/release/stubs.md index c1c5bd47f..48594128b 100644 --- a/docs/release-source/release/stubs.md +++ b/docs/release-source/release/stubs.md @@ -271,6 +271,11 @@ Causes the stub to throw an exception of the provided type. Causes the stub to throw the provided exception object. +#### `stub.throws(function() { return new Error(); });` + +Causes the stub to throw the exception returned by the function. + + #### `stub.rejects();` Causes the stub to return a Promise which rejects with an exception (`Error`). diff --git a/lib/sinon/behavior.js b/lib/sinon/behavior.js index 489da3b36..640261fe8 100644 --- a/lib/sinon/behavior.js +++ b/lib/sinon/behavior.js @@ -127,6 +127,10 @@ var proto = { if (this.exception) { throw this.exception; + } else if (this.exceptionCreator) { + this.exception = this.exceptionCreator(); + this.exceptionCreator = undefined; + throw this.exception; } else if (typeof this.returnArgAt === "number") { return args[this.returnArgAt]; } else if (this.returnThis) { diff --git a/lib/sinon/default-behaviors.js b/lib/sinon/default-behaviors.js index 13a8ce7b7..46e4f160e 100644 --- a/lib/sinon/default-behaviors.js +++ b/lib/sinon/default-behaviors.js @@ -7,11 +7,18 @@ var useLeftMostCallback = -1; var useRightMostCallback = -2; function throwsException(fake, error, message) { - if (typeof error === "string") { - fake.exception = new Error(message || ""); - fake.exception.name = error; + if (typeof error === "function") { + fake.exceptionCreator = error; + } else if (typeof error === "string") { + fake.exceptionCreator = function () { + var newException = new Error(message || ""); + newException.name = error; + return newException; + }; } else if (!error) { - fake.exception = new Error("Error"); + fake.exceptionCreator = function () { + return new Error("Error"); + }; } else { fake.exception = error; } @@ -129,6 +136,7 @@ module.exports = { fake.reject = false; fake.returnValueDefined = true; fake.exception = undefined; + fake.exceptionCreator = undefined; fake.fakeFn = undefined; }, @@ -158,6 +166,7 @@ module.exports = { fake.reject = false; fake.returnValueDefined = true; fake.exception = undefined; + fake.exceptionCreator = undefined; fake.fakeFn = undefined; }, @@ -176,6 +185,7 @@ module.exports = { fake.reject = true; fake.returnValueDefined = true; fake.exception = undefined; + fake.exceptionCreator = undefined; fake.fakeFn = undefined; return fake; diff --git a/test/stub-test.js b/test/stub-test.js index 19579aebc..0c2aa60fb 100644 --- a/test/stub-test.js +++ b/test/stub-test.js @@ -513,6 +513,80 @@ describe("stub", function () { assert.exception(stub, "Error"); }); + it("throws an exception created using a function", function () { + var stub = createStub.create(); + + stub.throws(function () { + return new Error("not implemented"); + }); + + assert.exception(stub, { + message: "not implemented" + }); + assert.same(stub.firstCall.exception.message, "not implemented"); + assert.contains(stub.firstCall.toString(), "not implemented"); + }); + + describe("lazy instantiation of exceptions", function () { + var errorSpy; + beforeEach(function () { + this.originalError = global.Error; + errorSpy = createSpy(global, "Error"); + // errorSpy starts with a call already made, not sure why + errorSpy.reset(); + }); + + afterEach(function () { + errorSpy.restore(); + global.Error = this.originalError; + }); + + it("uses a lazily created exception for the generic error", function () { + var stub = createStub.create(); + stub.throws(); + + assert.isFalse(errorSpy.called); + assert.exception(stub, "Error"); + assert.isTrue(errorSpy.called); + }); + + it("uses a lazily created exception for the named error", function () { + var stub = createStub.create(); + stub.throws("TypeError", "typeerror message"); + + assert.isFalse(errorSpy.called); + assert.exception(stub, { + name: "TypeError", + message: "typeerror message" + }); + assert.isTrue(errorSpy.called); + }); + + it("uses a lazily created exception provided by a function", function () { + var stub = createStub.create(); + + stub.throws(function () { + return new Error("not implemented"); + }); + + assert.isFalse(errorSpy.called); + assert.exception(stub, { + message: "not implemented" + }); + assert.isTrue(errorSpy.called); + }); + + it("does not use a lazily created exception if the error object is provided", function () { + var stub = createStub.create(); + var exception = new Error(); + stub.throws(exception); + + assert.same(errorSpy.callCount, 1); + assert.exception(stub, exception); + assert.same(errorSpy.callCount, 1); + }); + }); + it("resets 'invoking' flag", function () { var stub = createStub.create(); stub.throws(); @@ -872,6 +946,17 @@ describe("stub", function () { assert(stub.threw("TypeError")); }); + it("handles threw properly for lazily instantiated Errors", function () { + var stub = createStub(this.object, "method"); + stub.throws(function () { + return new TypeError(); + }); + + assert.exception(this.object.method); + + assert(stub.threw("TypeError")); + }); + it("returns standalone stub without arguments", function () { var stub = createStub();