Skip to content

Commit 3babc5b

Browse 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

4 files changed

Lines changed: 207 additions & 15 deletions

File tree

‎doc/api/assert.md‎

Lines changed: 80 additions & 0 deletions
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+
awaitassert.doesNotReject(
265+
async () => {
266+
thrownewTypeError('Wrong value');
267+
},
268+
SyntaxError
269+
);
270+
})();
271+
```
272+
273+
```js
274+
assert.doesNotReject(
275+
() =>Promise.reject(newTypeError('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+
awaitassert.rejects(
696+
async () => {
697+
thrownewError('Wrong value');
698+
},
699+
Error
700+
);
701+
})();
702+
```
703+
704+
```js
705+
assert.rejects(
706+
() =>Promise.reject(newError('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

‎lib/assert.js‎

Lines 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=functionthrows(block,error,message){
723-
constactual=getActual(block);
721+
asyncfunctionwaitForActual(block){
722+
if(typeofblock!=='function'){
723+
thrownewerrors.ERR_INVALID_ARG_TYPE('block','Function',block);
724+
}
725+
try{
726+
awaitblock();
727+
}catch(e){
728+
returne;
729+
}
730+
returnerrors.NO_EXCEPTION_SENTINEL;
731+
}
724732

733+
// Expected to throw an error.
734+
functionexpectsError(stackStartFn,actual,error,message){
725735
if(typeoferror==='string'){
726-
if(arguments.length===3)
736+
if(arguments.length===4){
727737
thrownewerrors.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+
constfnType=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
throwactual;
752763
}
753-
};
764+
}
754765

755-
assert.doesNotThrow=functiondoesNotThrow(block,error,message){
756-
constactual=getActual(block);
766+
functionexpectsNoError(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
constdetails=message ? `: ${message}` : '.';
777+
constfnType=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
throwactual;
776-
};
787+
}
788+
789+
functionthrows(block, ...args){
790+
expectsError(throws,getActual(block), ...args);
791+
}
792+
793+
assert.throws=throws;
794+
795+
asyncfunctionrejects(block, ...args){
796+
expectsError(rejects,awaitwaitForActual(block), ...args);
797+
}
798+
799+
assert.rejects=rejects;
800+
801+
functiondoesNotThrow(block, ...args){
802+
expectsNoError(doesNotThrow,getActual(block), ...args);
803+
}
804+
805+
assert.doesNotThrow=doesNotThrow;
806+
807+
asyncfunctiondoesNotReject(block, ...args){
808+
expectsNoError(doesNotReject,awaitwaitForActual(block), ...args);
809+
}
810+
811+
assert.doesNotReject=doesNotReject;
777812

778813
assert.ifError=functionifError(err){if(err)throwerr;};
779814

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
constcommon=require('../common');
3+
constassert=require('assert');
4+
const{ promisify }=require('util');
5+
constwait=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+
constpromise=assert.rejects(async()=>{
25+
awaitwait(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+
constpromise=assert.doesNotReject(async()=>{
37+
awaitwait(1);
38+
thrownewError();
39+
});
40+
assert.rejects(()=>promise,
41+
(err)=>{
42+
assert(errinstanceofassert.AssertionError,
43+
`${err.name} is not instance of AssertionError`);
44+
assert.strictEqual(err.code,'ERR_ASSERTION');
45+
assert(/^Gotunwantedrejection\.\n$/.test(err.message));
46+
assert.strictEqual(err.operator,'doesNotReject');
47+
assert.ok(!err.stack.includes('at Function.doesNotReject'));
48+
returntrue;
49+
}
50+
);
51+
}
52+
53+
{
54+
constpromise=assert.rejects(()=>{});
55+
assert.rejects(()=>promise,
56+
(err)=>{
57+
assert(errinstanceofassert.AssertionError,
58+
`${err.name} is not instance of AssertionError`);
59+
assert.strictEqual(err.code,'ERR_ASSERTION');
60+
assert(/^Missingexpectedrejection\.$/.test(err.message));
61+
assert.strictEqual(err.operator,'rejects');
62+
assert.ok(!err.stack.includes('at Function.rejects'));
63+
returntrue;
64+
}
65+
);
66+
}

‎test/parallel/test-assert.js‎

Lines 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(einstanceofa.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: /^Missingexpectedexception\(TypeError\):fhqwhgads$/
546547
}));
548+
549+
letthrew=false;
550+
try{
551+
a.throws(noop);
552+
}catch(e){
553+
threw=true;
554+
assert.ok(einstanceofa.AssertionError);
555+
assert.ok(!e.stack.includes('at Function.throws'));
556+
}
557+
assert.ok(threw);
547558
}
548559

549560
constcircular={y: 1};

0 commit comments

Comments
 (0)