mirror of https://github.com/nodejs/node.git
assert: add rejects() and doesNotReject()
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). 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>
This commit is contained in:
parent
580ad0157a
commit
599337f43e
|
@ -312,6 +312,43 @@ parameter is undefined, a default error message is assigned. If the `message`
|
||||||
parameter is an instance of an [`Error`][] then it will be thrown instead of the
|
parameter is an instance of an [`Error`][] then it will be thrown instead of the
|
||||||
`AssertionError`.
|
`AssertionError`.
|
||||||
|
|
||||||
|
## 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])
|
## assert.doesNotThrow(block[, error][, message])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.21
|
added: v0.1.21
|
||||||
|
@ -841,6 +878,48 @@ If the values are not strictly equal, an `AssertionError` is thrown with a
|
||||||
`message` parameter is an instance of an [`Error`][] then it will be thrown
|
`message` parameter is an instance of an [`Error`][] then it will be thrown
|
||||||
instead of the `AssertionError`.
|
instead of the `AssertionError`.
|
||||||
|
|
||||||
|
## 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])
|
## assert.throws(block[, error][, message])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.21
|
added: v0.1.21
|
||||||
|
@ -977,6 +1056,7 @@ second argument. This might lead to difficult-to-spot errors.
|
||||||
[`assert.ok()`]: #assert_assert_ok_value_message
|
[`assert.ok()`]: #assert_assert_ok_value_message
|
||||||
[`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message
|
[`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message
|
||||||
[`assert.throws()`]: #assert_assert_throws_block_error_message
|
[`assert.throws()`]: #assert_assert_throws_block_error_message
|
||||||
|
[`assert.rejects()`]: #assert_assert_rejects_block_error_message
|
||||||
[`strict mode`]: #assert_strict_mode
|
[`strict mode`]: #assert_strict_mode
|
||||||
[Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
|
[Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
|
||||||
[Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring
|
[Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring
|
||||||
|
|
|
@ -425,14 +425,23 @@ function getActual(block) {
|
||||||
return NO_EXCEPTION_SENTINEL;
|
return NO_EXCEPTION_SENTINEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected to throw an error.
|
async function waitForActual(block) {
|
||||||
assert.throws = function throws(block, error, message) {
|
if (typeof block !== 'function') {
|
||||||
const actual = getActual(block);
|
throw new ERR_INVALID_ARG_TYPE('block', 'Function', block);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await block();
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return NO_EXCEPTION_SENTINEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectsError(stackStartFn, actual, error, message) {
|
||||||
if (typeof error === 'string') {
|
if (typeof error === 'string') {
|
||||||
if (arguments.length === 3)
|
if (arguments.length === 4) {
|
||||||
throw new ERR_INVALID_ARG_TYPE('error', ['Function', 'RegExp'], error);
|
throw new ERR_INVALID_ARG_TYPE('error', ['Function', 'RegExp'], error);
|
||||||
|
}
|
||||||
message = error;
|
message = error;
|
||||||
error = null;
|
error = null;
|
||||||
}
|
}
|
||||||
|
@ -443,21 +452,21 @@ assert.throws = function throws(block, error, message) {
|
||||||
details += ` (${error.name})`;
|
details += ` (${error.name})`;
|
||||||
}
|
}
|
||||||
details += message ? `: ${message}` : '.';
|
details += message ? `: ${message}` : '.';
|
||||||
|
const fnType = stackStartFn === rejects ? 'rejection' : 'exception';
|
||||||
innerFail({
|
innerFail({
|
||||||
actual,
|
actual,
|
||||||
expected: error,
|
expected: error,
|
||||||
operator: 'throws',
|
operator: stackStartFn.name,
|
||||||
message: `Missing expected exception${details}`,
|
message: `Missing expected ${fnType}${details}`,
|
||||||
stackStartFn: throws
|
stackStartFn
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (error && expectedException(actual, error, message) === false) {
|
if (error && expectedException(actual, error, message) === false) {
|
||||||
throw actual;
|
throw actual;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
assert.doesNotThrow = function doesNotThrow(block, error, message) {
|
function expectsNoError(stackStartFn, actual, error, message) {
|
||||||
const actual = getActual(block);
|
|
||||||
if (actual === NO_EXCEPTION_SENTINEL)
|
if (actual === NO_EXCEPTION_SENTINEL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -468,16 +477,41 @@ assert.doesNotThrow = function doesNotThrow(block, error, message) {
|
||||||
|
|
||||||
if (!error || expectedException(actual, error)) {
|
if (!error || expectedException(actual, error)) {
|
||||||
const details = message ? `: ${message}` : '.';
|
const details = message ? `: ${message}` : '.';
|
||||||
|
const fnType = stackStartFn === doesNotReject ? 'rejection' : 'exception';
|
||||||
innerFail({
|
innerFail({
|
||||||
actual,
|
actual,
|
||||||
expected: error,
|
expected: error,
|
||||||
operator: 'doesNotThrow',
|
operator: stackStartFn.name,
|
||||||
message: `Got unwanted exception${details}\n${actual && actual.message}`,
|
message: `Got unwanted ${fnType}${details}\n${actual && actual.message}`,
|
||||||
stackStartFn: doesNotThrow
|
stackStartFn
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
throw actual;
|
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) {
|
assert.ifError = function ifError(err) {
|
||||||
if (err !== null && err !== undefined) {
|
if (err !== null && err !== undefined) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -116,6 +116,7 @@ assert.throws(() => thrower(TypeError));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
threw = true;
|
threw = true;
|
||||||
assert.ok(e instanceof a.AssertionError);
|
assert.ok(e instanceof a.AssertionError);
|
||||||
|
assert.ok(!e.stack.includes('at Function.doesNotThrow'));
|
||||||
}
|
}
|
||||||
assert.ok(threw, 'a.doesNotThrow is not catching type matching errors');
|
assert.ok(threw, 'a.doesNotThrow is not catching type matching errors');
|
||||||
}
|
}
|
||||||
|
@ -221,6 +222,16 @@ a.throws(() => thrower(TypeError), (err) => {
|
||||||
code: 'ERR_ASSERTION',
|
code: 'ERR_ASSERTION',
|
||||||
message: /^Missing expected exception \(TypeError\): fhqwhgads$/
|
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 };
|
const circular = { y: 1 };
|
||||||
|
|
Loading…
Reference in New Issue