Skip to content

Commit 147aeed

Browse files
BridgeARMylesBorins
authored andcommitted
assert: improve assert.throws
Throw a TypeError in case a error message is provided in the second argument and a third argument is present as well. This is clearly a mistake and should not be done. Backport-PR-URL: #23223 PR-URL: #17585 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Evan Lucas <[email protected]>
1 parent c9d84b6 commit 147aeed

File tree

3 files changed

+93
-52
lines changed

3 files changed

+93
-52
lines changed

‎doc/api/assert.md‎

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -691,17 +691,42 @@ assert.throws(
691691

692692
Note that `error` can not be a string. If a string is provided as the second
693693
argument, then `error` is assumed to be omitted and the string will be used for
694-
`message` instead. This can lead to easy-to-miss mistakes:
694+
`message` instead. This can lead to easy-to-miss mistakes. Please read the
695+
example below carefully if using a string as the second argument gets
696+
considered:
695697

696698
<!-- eslint-disable no-restricted-syntax -->
697699
```js
698-
// THIS IS A MISTAKE! DO NOT DO THIS!
699-
assert.throws(myFunction, 'missing foo', 'did not throw with expected message');
700-
701-
// Do this instead.
702-
assert.throws(myFunction,/missing foo/, 'did not throw with expected message');
700+
functionthrowingFirst(){
701+
thrownewError('First');
702+
}
703+
functionthrowingSecond(){
704+
thrownewError('Second');
705+
}
706+
functionnotThrowing(){}
707+
708+
// The second argument is a string and the input function threw an Error.
709+
// In that case both cases do not throw as neither is going to try to
710+
// match for the error message thrown by the input function!
711+
assert.throws(throwingFirst, 'Second');
712+
assert.throws(throwingSecond, 'Second');
713+
714+
// The string is only used (as message) in case the function does not throw:
715+
assert.throws(notThrowing, 'Second');
716+
// AssertionError [ERR_ASSERTION]: Missing expected exception: Second
717+
718+
// If it was intended to match for the error message do this instead:
719+
assert.throws(throwingSecond,/Second$/);
720+
// Does not throw because the error messages match.
721+
assert.throws(throwingFirst,/Second$/);
722+
// Throws a error:
723+
// Error: First
724+
// at throwingFirst (repl:2:9)
703725
```
704726

727+
Due to the confusing notation, it is recommended not to use a string as the
728+
second argument. This might lead to difficult-to-spot errors.
729+
705730
## Caveats
706731

707732
For the following cases, consider using ES2015 [`Object.is()`][],

‎lib/assert.js‎

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -675,68 +675,73 @@ function expectedException(actual, expected){
675675
returnexpected.call({},actual)===true;
676676
}
677677

678-
functiontryBlock(block){
678+
functiongetActual(block){
679+
if(typeofblock!=='function'){
680+
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE','block','Function',
681+
block);
682+
}
679683
try{
680684
block();
681685
}catch(e){
682686
returne;
683687
}
684688
}
685689

686-
functioninnerThrows(shouldThrow,block,expected,message){
687-
vardetails='';
690+
// Expected to throw an error.
691+
assert.throws=functionthrows(block,error,message){
692+
constactual=getActual(block);
688693

689-
if(typeofblock!=='function'){
690-
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE','block','function',
691-
block);
692-
}
694+
if(typeoferror==='string'){
695+
if(arguments.length===3)
696+
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE',
697+
'error',
698+
['Function','RegExp'],
699+
error);
693700

694-
if(typeofexpected==='string'){
695-
message=expected;
696-
expected=null;
701+
message=error;
702+
error=null;
697703
}
698704

699-
constactual=tryBlock(block);
700-
701-
if(shouldThrow===true){
702-
if(actual===undefined){
703-
if(expected&&expected.name){
704-
details+=` (${expected.name})`;
705-
}
706-
details+=message ? `: ${message}` : '.';
707-
innerFail({
708-
actual,
709-
expected,
710-
operator: 'throws',
711-
message: `Missing expected exception${details}`,
712-
stackStartFn: assert.throws
713-
});
714-
}
715-
if(expected&&expectedException(actual,expected)===false){
716-
throwactual;
717-
}
718-
}elseif(actual!==undefined){
719-
if(!expected||expectedException(actual,expected)){
720-
details=message ? `: ${message}` : '.';
721-
innerFail({
722-
actual,
723-
expected,
724-
operator: 'doesNotThrow',
725-
message: `Got unwanted exception${details}`,
726-
stackStartFn: assert.doesNotThrow
727-
});
705+
if(actual===undefined){
706+
letdetails='';
707+
if(error&&error.name){
708+
details+=` (${error.name})`;
728709
}
710+
details+=message ? `: ${message}` : '.';
711+
innerFail({
712+
actual,
713+
expected: error,
714+
operator: 'throws',
715+
message: `Missing expected exception${details}`,
716+
stackStartFn: throws
717+
});
718+
}
719+
if(error&&expectedException(actual,error)===false){
729720
throwactual;
730721
}
731-
}
732-
733-
// Expected to throw an error.
734-
assert.throws=functionthrows(block,error,message){
735-
innerThrows(true,block,error,message);
736722
};
737723

738724
assert.doesNotThrow=functiondoesNotThrow(block,error,message){
739-
innerThrows(false,block,error,message);
725+
constactual=getActual(block);
726+
if(actual===undefined)
727+
return;
728+
729+
if(typeoferror==='string'){
730+
message=error;
731+
error=null;
732+
}
733+
734+
if(!error||expectedException(actual,error)){
735+
constdetails=message ? `: ${message}` : '.';
736+
innerFail({
737+
actual,
738+
expected: error,
739+
operator: 'doesNotThrow',
740+
message: `Got unwanted exception${details}\n${actual.message}`,
741+
stackStartFn: doesNotThrow
742+
});
743+
}
744+
throwactual;
740745
};
741746

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

‎test/parallel/test-assert.js‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ try{
640640
common.expectsError({
641641
code: 'ERR_INVALID_ARG_TYPE',
642642
type: TypeError,
643-
message: 'The "block" argument must be of type function. Received '+
643+
message: 'The "block" argument must be of type Function. Received '+
644644
`type ${typeName(block)}`
645645
})(e);
646646
}
@@ -731,3 +731,14 @@ common.expectsError(
731731
message: 'null == true'
732732
}
733733
);
734+
735+
common.expectsError(
736+
// eslint-disable-next-line no-restricted-syntax
737+
()=>assert.throws(()=>{},'Error message','message'),
738+
{
739+
code: 'ERR_INVALID_ARG_TYPE',
740+
type: TypeError,
741+
message: 'The "error" argument must be one of type Function or RegExp. '+
742+
'Received type string'
743+
}
744+
);

0 commit comments

Comments
(0)