diff --git a/lib/assertion.js b/lib/assertion.js index 646344e..367e71b 100644 --- a/lib/assertion.js +++ b/lib/assertion.js @@ -849,15 +849,19 @@ Assertion.prototype.keys = function(keys) { /** * # .throw(constructor) * - * Assert that a function will throw a specific type of error (as determined using - * `instanceof`) and/or that the error thrown will match a RegExp or include a string. + * Assert that a function will throw a specific type of error, or specific type of error + * (as determined using `instanceof`), optionally with a RegExp or string inclusion test + * for the error's message. * - * var fn = function () { throw new ReferenceError('This is a bad function.'); } + * var err = new ReferenceError('This is a bad function.'); + * var fn = function () { throw err; } * expect(fn).to.throw(ReferenceError); * expect(fn).to.throw(Error); * expect(fn).to.throw(/bad function/); * expect(fn).to.not.throw('good function'); * expect(fn).to.throw(ReferenceError, /bad function/); + * expect(fn).to.throw(err); + * expect(fn).to.not.throw(new RangeError('Out of range.')); * * Please note that when a throw expectation is negated, it will check each * parameter independently, starting with error constructor type. The appropriate way @@ -877,7 +881,8 @@ Assertion.prototype.keys = function(keys) { Assertion.prototype.throw = function (constructor, msg) { new Assertion(this.obj).is.a('function'); - var thrown = false; + var thrown = false + , desiredError = null; if (arguments.length === 0) { msg = null; @@ -885,12 +890,25 @@ Assertion.prototype.throw = function (constructor, msg) { } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { msg = constructor; constructor = null; + } else if (constructor && constructor instanceof Error) { + desiredError = constructor; + constructor = null; + msg = null; } try { this.obj(); } catch (err) { - // first, check constructor + // first, check desired error + if (desiredError) { + this.assert( + err === desiredError + , 'expected ' + this.inspect + ' to throw ' + inspect(desiredError) + ' but ' + inspect(err) + ' was thrown' + , 'expected ' + this.inspect + ' to not throw ' + inspect(desiredError) + ); + return this; + } + // if we weren't passed one, check constructor if (constructor && 'function' === typeof constructor) { var name = (new constructor()).name; this.assert( @@ -919,12 +937,12 @@ Assertion.prototype.throw = function (constructor, msg) { } } - var name = (constructor ? constructor.name : 'an error'); + var expectedThrown = (constructor ? constructor.name : desiredError ? inspect(desiredError) : 'an error'); this.assert( thrown === true - , 'expected ' + this.inspect + ' to throw ' + name - , 'expected ' + this.inspect + ' to not throw ' + name); + , 'expected ' + this.inspect + ' to throw ' + expectedThrown + , 'expected ' + this.inspect + ' to not throw ' + expectedThrown); return this; }; diff --git a/test/expect.js b/test/expect.js index 4c6021c..34f9d8d 100644 --- a/test/expect.js +++ b/test/expect.js @@ -424,23 +424,31 @@ suite('expect', function () { }; PoorlyConstructedError.prototype = Object.create(Error.prototype); + var specificError = new RangeError('boo'); + var goodFn = function () { 1==1; } , badFn = function () { throw new Error('testing'); } , refErrFn = function () { throw new ReferenceError('hello'); } - , ickyErrFn = function () { throw new PoorlyConstructedError(); }; + , ickyErrFn = function () { throw new PoorlyConstructedError(); } + , specificErrFn = function () { throw specificError; }; expect(goodFn).to.not.throw(); expect(goodFn).to.not.throw(Error); + expect(goodFn).to.not.throw(specificError); expect(badFn).to.throw(); expect(badFn).to.throw(Error); expect(badFn).to.not.throw(ReferenceError); + expect(badFn).to.not.throw(specificError); expect(refErrFn).to.throw(); expect(refErrFn).to.throw(ReferenceError); expect(refErrFn).to.throw(Error); expect(refErrFn).to.not.throw(TypeError); + expect(refErrFn).to.not.throw(specificError); expect(ickyErrFn).to.throw(); expect(ickyErrFn).to.throw(PoorlyConstructedError); expect(ickyErrFn).to.throw(Error); + expect(ickyErrFn).to.not.throw(specificError); + expect(specificErrFn).to.throw(specificError); expect(badFn).to.throw(/testing/); expect(badFn).to.not.throw(/hello/); @@ -458,6 +466,10 @@ suite('expect', function () { expect(goodFn).to.throw(ReferenceError); }, "expected [Function] to throw ReferenceError"); + err(function(){ + expect(goodFn).to.throw(specificError); + }, "expected [Function] to throw [RangeError: boo]"); + err(function(){ expect(badFn).to.not.throw(); }, "expected [Function] to not throw an error"); @@ -466,6 +478,10 @@ suite('expect', function () { expect(badFn).to.throw(ReferenceError); }, "expected [Function] to throw ReferenceError but a Error was thrown"); + err(function(){ + expect(badFn).to.throw(specificError); + }, "expected [Function] to throw [RangeError: boo] but [Error: testing] was thrown"); + err(function(){ expect(badFn).to.not.throw(Error); }, "expected [Function] to not throw Error"); @@ -486,6 +502,14 @@ suite('expect', function () { expect(ickyErrFn).to.throw(ReferenceError); }, "expected [Function] to throw ReferenceError but a PoorlyConstructedError was thrown"); + err(function(){ + expect(specificErrFn).to.throw(new ReferenceError('eek')); + }, "expected [Function] to throw [ReferenceError: eek] but [RangeError: boo] was thrown"); + + err(function(){ + expect(specificErrFn).to.not.throw(specificError); + }, "expected [Function] to not throw [RangeError: boo]"); + err(function (){ expect(badFn).to.not.throw(/testing/); }, "expected [Function] to throw error not matching /testing/"); diff --git a/test/should.js b/test/should.js index f433fd4..1f10cce 100644 --- a/test/should.js +++ b/test/should.js @@ -424,23 +424,31 @@ suite('should', function() { }; PoorlyConstructedError.prototype = Object.create(Error.prototype); + var specificError = new RangeError('boo'); + var goodFn = function () { 1==1; } , badFn = function () { throw new Error('testing'); } , refErrFn = function () { throw new ReferenceError('hello'); } - , ickyErrFn = function () { throw new PoorlyConstructedError(); }; + , ickyErrFn = function () { throw new PoorlyConstructedError(); } + , specificErrFn = function () { throw specificError; }; (goodFn).should.not.throw(); (goodFn).should.not.throw(Error); + (goodFn).should.not.throw(specificError); (badFn).should.throw(); (badFn).should.throw(Error); (badFn).should.not.throw(ReferenceError); + (badFn).should.not.throw(specificError); (refErrFn).should.throw(); (refErrFn).should.throw(ReferenceError); (refErrFn).should.throw(Error); (refErrFn).should.not.throw(TypeError); + (refErrFn).should.not.throw(specificError); (ickyErrFn).should.throw(); (ickyErrFn).should.throw(PoorlyConstructedError); (ickyErrFn).should.throw(Error); + (ickyErrFn).should.not.throw(specificError); + (specificErrFn).should.throw(specificError); (badFn).should.throw(/testing/); (badFn).should.throw('testing'); @@ -452,8 +460,10 @@ suite('should', function() { should.throw(refErrFn, ReferenceError); should.throw(refErrFn, Error); should.throw(ickyErrFn, PoorlyConstructedError); + should.throw(specificErrFn, specificError); should.not.throw(goodFn); should.not.throw(badFn, ReferenceError); + should.not.throw(badFn, specificError); should.throw(badFn, Error, /testing/); should.throw(badFn, Error, 'testing'); @@ -466,6 +476,10 @@ suite('should', function() { (goodFn).should.throw(ReferenceError); }, "expected [Function] to throw ReferenceError"); + err(function(){ + (goodFn).should.throw(specificError); + }, "expected [Function] to throw [RangeError: boo]"); + err(function(){ (badFn).should.not.throw(); }, "expected [Function] to not throw an error"); @@ -474,6 +488,10 @@ suite('should', function() { (badFn).should.throw(ReferenceError); }, "expected [Function] to throw ReferenceError but a Error was thrown"); + err(function(){ + (badFn).should.throw(specificError); + }, "expected [Function] to throw [RangeError: boo] but [Error: testing] was thrown"); + err(function(){ (badFn).should.not.throw(Error); }, "expected [Function] to not throw Error"); @@ -494,6 +512,14 @@ suite('should', function() { (ickyErrFn).should.throw(ReferenceError); }, "expected [Function] to throw ReferenceError but a PoorlyConstructedError was thrown"); + err(function(){ + (specificErrFn).should.throw(new ReferenceError('eek')); + }, "expected [Function] to throw [ReferenceError: eek] but [RangeError: boo] was thrown"); + + err(function(){ + (specificErrFn).should.not.throw(specificError); + }, "expected [Function] to not throw [RangeError: boo]"); + err(function (){ (badFn).should.not.throw(/testing/); }, "expected [Function] to throw error not matching /testing/");