diff options
author | feugy <damien.feugas@gmail.com> | 2018-01-12 02:16:41 +0300 |
---|---|---|
committer | Myles Borins <mylesborins@google.com> | 2018-11-04 15:30:51 +0300 |
commit | 3babc5bb533d94e6355d062233fbe05744603115 (patch) | |
tree | 37359302d360c32fc34a4b52d735b904f6dc903b | |
parent | 7f34c277accdfffe0b87b24f5e2fda889b1ac924 (diff) |
assert: add rejects() and doesNotReject()
Implement asynchronous equivalent for assert.throws() and
assert.doesNotThrow().
Backport-PR-URL: https://github.com/nodejs/node/pull/24019
PR-URL: https://github.com/nodejs/node/pull/18023
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Shingo Inoue <leko.noor@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
-rw-r--r-- | doc/api/assert.md | 80 | ||||
-rw-r--r-- | lib/assert.js | 65 | ||||
-rw-r--r-- | test/parallel/test-assert-async.js | 66 | ||||
-rw-r--r-- | test/parallel/test-assert.js | 11 |
4 files changed, 207 insertions, 15 deletions
diff --git a/doc/api/assert.md b/doc/api/assert.md index dbd32096b5c..6cc3cd561eb 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -242,6 +242,43 @@ If the values are not equal, an `AssertionError` is thrown with a `message` property set equal to the value of the `message` parameter. If the `message` parameter is undefined, a default error message is assigned. +## assert.doesNotReject(block[, error][, message]) +<!-- YAML +added: REPLACEME +--> +* `block` {Function} +* `error` {RegExp|Function} +* `message` {any} + +Awaits for the promise returned by function `block` to complete and not be +rejected. See [`assert.rejects()`][] for more details. + +When `assert.doesNotReject()` is called, it will immediately call the `block` +function, and awaits for completion. + +Besides the async nature to await the completion behaves identical to +[`assert.doesNotThrow()`][]. + +```js +(async () => { + await assert.doesNotReject( + async () => { + throw new TypeError('Wrong value'); + }, + SyntaxError + ); +})(); +``` + +```js +assert.doesNotReject( + () => Promise.reject(new TypeError('Wrong value')), + SyntaxError +).then(() => { + // ... +}); +``` + ## assert.doesNotThrow(block[, error][, message]) <!-- YAML added: v0.1.21 @@ -631,6 +668,48 @@ If the values are not strictly equal, an `AssertionError` is thrown with a `message` property set equal to the value of the `message` parameter. If the `message` parameter is undefined, a default error message is assigned. +## assert.rejects(block[, error][, message]) +<!-- YAML +added: REPLACEME +--> +* `block` {Function} +* `error` {RegExp|Function|Object} +* `message` {any} + +Awaits for promise returned by function `block` to be rejected. + +When `assert.rejects()` is called, it will immediately call the `block` +function, and awaits for completion. + +Besides the async nature to await the completion behaves identical to +[`assert.throws()`][]. + +If specified, `error` can be a constructor, [`RegExp`][], a validation +function, or an object where each property will be tested for. + +If specified, `message` will be the message provided by the `AssertionError` if +the block fails to reject. + +```js +(async () => { + await assert.rejects( + async () => { + throw new Error('Wrong value'); + }, + Error + ); +})(); +``` + +```js +assert.rejects( + () => Promise.reject(new Error('Wrong value')), + Error +).then(() => { + // ... +}); +``` + ## assert.throws(block[, error][, message]) <!-- YAML added: v0.1.21 @@ -786,6 +865,7 @@ For more information, see [`assert.ok()`]: #assert_assert_ok_value_message [`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message [`assert.throws()`]: #assert_assert_throws_block_error_message +[`assert.rejects()`]: #assert_assert_rejects_block_error_message [`strict mode`]: #assert_strict_mode [Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison [Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring diff --git a/lib/assert.js b/lib/assert.js index ad568547b45..545f76bef4b 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -718,17 +718,27 @@ function getActual(block) { } } -// Expected to throw an error. -assert.throws = function throws(block, error, message) { - const actual = getActual(block); +async function waitForActual(block) { + if (typeof block !== 'function') { + throw new errors.ERR_INVALID_ARG_TYPE('block', 'Function', block); + } + try { + await block(); + } catch (e) { + return e; + } + return errors.NO_EXCEPTION_SENTINEL; +} +// Expected to throw an error. +function expectsError(stackStartFn, actual, error, message) { if (typeof error === 'string') { - if (arguments.length === 3) + if (arguments.length === 4) { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'error', ['Function', 'RegExp'], error); - + } message = error; error = null; } @@ -739,21 +749,21 @@ assert.throws = function throws(block, error, message) { details += ` (${error.name})`; } details += message ? `: ${message}` : '.'; + const fnType = stackStartFn === rejects ? 'rejection' : 'exception'; innerFail({ actual, expected: error, - operator: 'throws', - message: `Missing expected exception${details}`, - stackStartFn: throws + operator: stackStartFn.name, + message: `Missing expected ${fnType}${details}`, + stackStartFn }); } if (error && expectedException(actual, error, message) === false) { throw actual; } -}; +} -assert.doesNotThrow = function doesNotThrow(block, error, message) { - const actual = getActual(block); +function expectsNoError(stackStartFn, actual, error, message) { if (actual === undefined) return; @@ -764,16 +774,41 @@ assert.doesNotThrow = function doesNotThrow(block, error, message) { if (!error || expectedException(actual, error)) { const details = message ? `: ${message}` : '.'; + const fnType = stackStartFn === doesNotReject ? 'rejection' : 'exception'; innerFail({ actual, expected: error, - operator: 'doesNotThrow', - message: `Got unwanted exception${details}\n${actual.message}`, - stackStartFn: doesNotThrow + operator: stackStartFn.name, + message: `Got unwanted ${fnType}${details}\n${actual.message}`, + stackStartFn }); } throw actual; -}; +} + +function throws(block, ...args) { + expectsError(throws, getActual(block), ...args); +} + +assert.throws = throws; + +async function rejects(block, ...args) { + expectsError(rejects, await waitForActual(block), ...args); +} + +assert.rejects = rejects; + +function doesNotThrow(block, ...args) { + expectsNoError(doesNotThrow, getActual(block), ...args); +} + +assert.doesNotThrow = doesNotThrow; + +async function doesNotReject(block, ...args) { + expectsNoError(doesNotReject, await waitForActual(block), ...args); +} + +assert.doesNotReject = doesNotReject; assert.ifError = function ifError(err) { if (err) throw err; }; diff --git a/test/parallel/test-assert-async.js b/test/parallel/test-assert-async.js new file mode 100644 index 00000000000..9fcde68bd5a --- /dev/null +++ b/test/parallel/test-assert-async.js @@ -0,0 +1,66 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { promisify } = require('util'); +const wait = promisify(setTimeout); + +/* eslint-disable prefer-common-expectserror, no-restricted-properties */ + +// Test assert.rejects() and assert.doesNotReject() by checking their +// expected output and by verifying that they do not work sync + +assert.rejects( + () => assert.fail(), + common.expectsError({ + code: 'ERR_ASSERTION', + type: assert.AssertionError, + message: 'Failed' + }) +); + +assert.doesNotReject(() => {}); + +{ + const promise = assert.rejects(async () => { + await wait(1); + assert.fail(); + }, common.expectsError({ + code: 'ERR_ASSERTION', + type: assert.AssertionError, + message: 'Failed' + })); + assert.doesNotReject(() => promise); +} + +{ + const promise = assert.doesNotReject(async () => { + await wait(1); + throw new Error(); + }); + assert.rejects(() => promise, + (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert(/^Got unwanted rejection\.\n$/.test(err.message)); + assert.strictEqual(err.operator, 'doesNotReject'); + assert.ok(!err.stack.includes('at Function.doesNotReject')); + return true; + } + ); +} + +{ + const promise = assert.rejects(() => {}); + assert.rejects(() => promise, + (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert(/^Missing expected rejection\.$/.test(err.message)); + assert.strictEqual(err.operator, 'rejects'); + assert.ok(!err.stack.includes('at Function.rejects')); + return true; + } + ); +} diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 08b81b7c37f..7335982a6c6 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -443,6 +443,7 @@ assert.throws(makeBlock(thrower, TypeError)); } catch (e) { threw = true; assert.ok(e instanceof a.AssertionError); + assert.ok(!e.stack.includes('at Function.doesNotThrow')); } assert.strictEqual(true, threw, 'a.doesNotThrow is not catching type matching errors'); @@ -544,6 +545,16 @@ a.throws(makeBlock(thrower, TypeError), (err) => { code: 'ERR_ASSERTION', message: /^Missing expected exception \(TypeError\): fhqwhgads$/ })); + + let threw = false; + try { + a.throws(noop); + } catch (e) { + threw = true; + assert.ok(e instanceof a.AssertionError); + assert.ok(!e.stack.includes('at Function.throws')); + } + assert.ok(threw); } const circular = { y: 1 }; |