Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3babc5b

Browse filesBrowse files
feugyMylesBorins
authored andcommitted
assert: add rejects() and doesNotReject()
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). Backport-PR-URL: #24019 PR-URL: #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>
1 parent 7f34c27 commit 3babc5b
Copy full SHA for 3babc5b

File tree

Expand file treeCollapse file tree

4 files changed

+207
-15
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+207
-15
lines changed
Open diff view settings
Collapse file

‎doc/api/assert.md‎

Copy file name to clipboardExpand all lines: doc/api/assert.md
+80Lines changed: 80 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,43 @@ If the values are not equal, an `AssertionError` is thrown with a `message`
242242
property set equal to the value of the `message` parameter. If the `message`
243243
parameter is undefined, a default error message is assigned.
244244

245+
## assert.doesNotReject(block[, error][, message])
246+
<!-- YAML
247+
added: REPLACEME
248+
-->
249+
* `block` {Function}
250+
* `error` {RegExp|Function}
251+
* `message` {any}
252+
253+
Awaits for the promise returned by function `block` to complete and not be
254+
rejected. See [`assert.rejects()`][] for more details.
255+
256+
When `assert.doesNotReject()` is called, it will immediately call the `block`
257+
function, and awaits for completion.
258+
259+
Besides the async nature to await the completion behaves identical to
260+
[`assert.doesNotThrow()`][].
261+
262+
```js
263+
(async () => {
264+
await assert.doesNotReject(
265+
async () => {
266+
throw new TypeError('Wrong value');
267+
},
268+
SyntaxError
269+
);
270+
})();
271+
```
272+
273+
```js
274+
assert.doesNotReject(
275+
() => Promise.reject(new TypeError('Wrong value')),
276+
SyntaxError
277+
).then(() => {
278+
// ...
279+
});
280+
```
281+
245282
## assert.doesNotThrow(block[, error][, message])
246283
<!-- YAML
247284
added: v0.1.21
@@ -631,6 +668,48 @@ If the values are not strictly equal, an `AssertionError` is thrown with a
631668
`message` property set equal to the value of the `message` parameter. If the
632669
`message` parameter is undefined, a default error message is assigned.
633670

671+
## assert.rejects(block[, error][, message])
672+
<!-- YAML
673+
added: REPLACEME
674+
-->
675+
* `block` {Function}
676+
* `error` {RegExp|Function|Object}
677+
* `message` {any}
678+
679+
Awaits for promise returned by function `block` to be rejected.
680+
681+
When `assert.rejects()` is called, it will immediately call the `block`
682+
function, and awaits for completion.
683+
684+
Besides the async nature to await the completion behaves identical to
685+
[`assert.throws()`][].
686+
687+
If specified, `error` can be a constructor, [`RegExp`][], a validation
688+
function, or an object where each property will be tested for.
689+
690+
If specified, `message` will be the message provided by the `AssertionError` if
691+
the block fails to reject.
692+
693+
```js
694+
(async () => {
695+
await assert.rejects(
696+
async () => {
697+
throw new Error('Wrong value');
698+
},
699+
Error
700+
);
701+
})();
702+
```
703+
704+
```js
705+
assert.rejects(
706+
() => Promise.reject(new Error('Wrong value')),
707+
Error
708+
).then(() => {
709+
// ...
710+
});
711+
```
712+
634713
## assert.throws(block[, error][, message])
635714
<!-- YAML
636715
added: v0.1.21
@@ -786,6 +865,7 @@ For more information, see
786865
[`assert.ok()`]: #assert_assert_ok_value_message
787866
[`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message
788867
[`assert.throws()`]: #assert_assert_throws_block_error_message
868+
[`assert.rejects()`]: #assert_assert_rejects_block_error_message
789869
[`strict mode`]: #assert_strict_mode
790870
[Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
791871
[Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring
Collapse file

‎lib/assert.js‎

Copy file name to clipboardExpand all lines: lib/assert.js
+50-15Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -718,17 +718,27 @@ function getActual(block) {
718718
}
719719
}
720720

721-
// Expected to throw an error.
722-
assert.throws = function throws(block, error, message) {
723-
const actual = getActual(block);
721+
async function waitForActual(block) {
722+
if (typeof block !== 'function') {
723+
throw new errors.ERR_INVALID_ARG_TYPE('block', 'Function', block);
724+
}
725+
try {
726+
await block();
727+
} catch (e) {
728+
return e;
729+
}
730+
return errors.NO_EXCEPTION_SENTINEL;
731+
}
724732

733+
// Expected to throw an error.
734+
function expectsError(stackStartFn, actual, error, message) {
725735
if (typeof error === 'string') {
726-
if (arguments.length === 3)
736+
if (arguments.length === 4) {
727737
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
728738
'error',
729739
['Function', 'RegExp'],
730740
error);
731-
741+
}
732742
message = error;
733743
error = null;
734744
}
@@ -739,21 +749,21 @@ assert.throws = function throws(block, error, message) {
739749
details += ` (${error.name})`;
740750
}
741751
details += message ? `: ${message}` : '.';
752+
const fnType = stackStartFn === rejects ? 'rejection' : 'exception';
742753
innerFail({
743754
actual,
744755
expected: error,
745-
operator: 'throws',
746-
message: `Missing expected exception${details}`,
747-
stackStartFn: throws
756+
operator: stackStartFn.name,
757+
message: `Missing expected ${fnType}${details}`,
758+
stackStartFn
748759
});
749760
}
750761
if (error && expectedException(actual, error, message) === false) {
751762
throw actual;
752763
}
753-
};
764+
}
754765

755-
assert.doesNotThrow = function doesNotThrow(block, error, message) {
756-
const actual = getActual(block);
766+
function expectsNoError(stackStartFn, actual, error, message) {
757767
if (actual === undefined)
758768
return;
759769

@@ -764,16 +774,41 @@ assert.doesNotThrow = function doesNotThrow(block, error, message) {
764774

765775
if (!error || expectedException(actual, error)) {
766776
const details = message ? `: ${message}` : '.';
777+
const fnType = stackStartFn === doesNotReject ? 'rejection' : 'exception';
767778
innerFail({
768779
actual,
769780
expected: error,
770-
operator: 'doesNotThrow',
771-
message: `Got unwanted exception${details}\n${actual.message}`,
772-
stackStartFn: doesNotThrow
781+
operator: stackStartFn.name,
782+
message: `Got unwanted ${fnType}${details}\n${actual.message}`,
783+
stackStartFn
773784
});
774785
}
775786
throw actual;
776-
};
787+
}
788+
789+
function throws(block, ...args) {
790+
expectsError(throws, getActual(block), ...args);
791+
}
792+
793+
assert.throws = throws;
794+
795+
async function rejects(block, ...args) {
796+
expectsError(rejects, await waitForActual(block), ...args);
797+
}
798+
799+
assert.rejects = rejects;
800+
801+
function doesNotThrow(block, ...args) {
802+
expectsNoError(doesNotThrow, getActual(block), ...args);
803+
}
804+
805+
assert.doesNotThrow = doesNotThrow;
806+
807+
async function doesNotReject(block, ...args) {
808+
expectsNoError(doesNotReject, await waitForActual(block), ...args);
809+
}
810+
811+
assert.doesNotReject = doesNotReject;
777812

778813
assert.ifError = function ifError(err) { if (err) throw err; };
779814

Collapse file

‎test/parallel/test-assert-async.js‎

Copy file name to clipboard
+66Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const { promisify } = require('util');
5+
const wait = promisify(setTimeout);
6+
7+
/* eslint-disable prefer-common-expectserror, no-restricted-properties */
8+
9+
// Test assert.rejects() and assert.doesNotReject() by checking their
10+
// expected output and by verifying that they do not work sync
11+
12+
assert.rejects(
13+
() => assert.fail(),
14+
common.expectsError({
15+
code: 'ERR_ASSERTION',
16+
type: assert.AssertionError,
17+
message: 'Failed'
18+
})
19+
);
20+
21+
assert.doesNotReject(() => {});
22+
23+
{
24+
const promise = assert.rejects(async () => {
25+
await wait(1);
26+
assert.fail();
27+
}, common.expectsError({
28+
code: 'ERR_ASSERTION',
29+
type: assert.AssertionError,
30+
message: 'Failed'
31+
}));
32+
assert.doesNotReject(() => promise);
33+
}
34+
35+
{
36+
const promise = assert.doesNotReject(async () => {
37+
await wait(1);
38+
throw new Error();
39+
});
40+
assert.rejects(() => promise,
41+
(err) => {
42+
assert(err instanceof assert.AssertionError,
43+
`${err.name} is not instance of AssertionError`);
44+
assert.strictEqual(err.code, 'ERR_ASSERTION');
45+
assert(/^Got unwanted rejection\.\n$/.test(err.message));
46+
assert.strictEqual(err.operator, 'doesNotReject');
47+
assert.ok(!err.stack.includes('at Function.doesNotReject'));
48+
return true;
49+
}
50+
);
51+
}
52+
53+
{
54+
const promise = assert.rejects(() => {});
55+
assert.rejects(() => promise,
56+
(err) => {
57+
assert(err instanceof assert.AssertionError,
58+
`${err.name} is not instance of AssertionError`);
59+
assert.strictEqual(err.code, 'ERR_ASSERTION');
60+
assert(/^Missing expected rejection\.$/.test(err.message));
61+
assert.strictEqual(err.operator, 'rejects');
62+
assert.ok(!err.stack.includes('at Function.rejects'));
63+
return true;
64+
}
65+
);
66+
}
Collapse file

‎test/parallel/test-assert.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-assert.js
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ assert.throws(makeBlock(thrower, TypeError));
443443
} catch (e) {
444444
threw = true;
445445
assert.ok(e instanceof a.AssertionError);
446+
assert.ok(!e.stack.includes('at Function.doesNotThrow'));
446447
}
447448
assert.strictEqual(true, threw,
448449
'a.doesNotThrow is not catching type matching errors');
@@ -544,6 +545,16 @@ a.throws(makeBlock(thrower, TypeError), (err) => {
544545
code: 'ERR_ASSERTION',
545546
message: /^Missing expected exception \(TypeError\): fhqwhgads$/
546547
}));
548+
549+
let threw = false;
550+
try {
551+
a.throws(noop);
552+
} catch (e) {
553+
threw = true;
554+
assert.ok(e instanceof a.AssertionError);
555+
assert.ok(!e.stack.includes('at Function.throws'));
556+
}
557+
assert.ok(threw);
547558
}
548559

549560
const circular = { y: 1 };

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.