Skip to content

Commit 612ba1a

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: #19230 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 1e5c7e3 commit 612ba1a

File tree

3 files changed

+92
-51
lines changed

3 files changed

+92
-51
lines changed

‎doc/api/assert.md‎

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

766766
Note that `error` can not be a string. If a string is provided as the second
767767
argument, then `error` is assumed to be omitted and the string will be used for
768-
`message` instead. This can lead to easy-to-miss mistakes:
768+
`message` instead. This can lead to easy-to-miss mistakes. Please read the
769+
example below carefully if using a string as the second argument gets
770+
considered:
769771

770772
<!-- eslint-disable no-restricted-syntax -->
771773
```js
772-
// THIS IS A MISTAKE! DO NOT DO THIS!
773-
assert.throws(myFunction, 'missing foo', 'did not throw with expected message');
774-
775-
// Do this instead.
776-
assert.throws(myFunction,/missing foo/, 'did not throw with expected message');
774+
functionthrowingFirst(){
775+
thrownewError('First');
776+
}
777+
functionthrowingSecond(){
778+
thrownewError('Second');
779+
}
780+
functionnotThrowing(){}
781+
782+
// The second argument is a string and the input function threw an Error.
783+
// In that case both cases do not throw as neither is going to try to
784+
// match for the error message thrown by the input function!
785+
assert.throws(throwingFirst, 'Second');
786+
assert.throws(throwingSecond, 'Second');
787+
788+
// The string is only used (as message) in case the function does not throw:
789+
assert.throws(notThrowing, 'Second');
790+
// AssertionError [ERR_ASSERTION]: Missing expected exception: Second
791+
792+
// If it was intended to match for the error message do this instead:
793+
assert.throws(throwingSecond,/Second$/);
794+
// Does not throw because the error messages match.
795+
assert.throws(throwingFirst,/Second$/);
796+
// Throws a error:
797+
// Error: First
798+
// at throwingFirst (repl:2:9)
777799
```
778800

801+
Due to the confusing notation, it is recommended not to use a string as the
802+
second argument. This might lead to difficult-to-spot errors.
803+
779804
## Caveats
780805

781806
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
@@ -211,68 +211,73 @@ function expectedException(actual, expected){
211211
returnexpected.call({},actual)===true;
212212
}
213213

214-
functiontryBlock(block){
214+
functiongetActual(block){
215+
if(typeofblock!=='function'){
216+
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE','block','Function',
217+
block);
218+
}
215219
try{
216220
block();
217221
}catch(e){
218222
returne;
219223
}
220224
}
221225

222-
functioninnerThrows(shouldThrow,block,expected,message){
223-
vardetails='';
226+
// Expected to throw an error.
227+
assert.throws=functionthrows(block,error,message){
228+
constactual=getActual(block);
224229

225-
if(typeofblock!=='function'){
226-
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE','block','Function',
227-
block);
228-
}
230+
if(typeoferror==='string'){
231+
if(arguments.length===3)
232+
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE',
233+
'error',
234+
['Function','RegExp'],
235+
error);
229236

230-
if(typeofexpected==='string'){
231-
message=expected;
232-
expected=null;
237+
message=error;
238+
error=null;
233239
}
234240

235-
constactual=tryBlock(block);
236-
237-
if(shouldThrow===true){
238-
if(actual===undefined){
239-
if(expected&&expected.name){
240-
details+=` (${expected.name})`;
241-
}
242-
details+=message ? `: ${message}` : '.';
243-
innerFail({
244-
actual,
245-
expected,
246-
operator: 'throws',
247-
message: `Missing expected exception${details}`,
248-
stackStartFn: assert.throws
249-
});
250-
}
251-
if(expected&&expectedException(actual,expected)===false){
252-
throwactual;
253-
}
254-
}elseif(actual!==undefined){
255-
if(!expected||expectedException(actual,expected)){
256-
details=message ? `: ${message}` : '.';
257-
innerFail({
258-
actual,
259-
expected,
260-
operator: 'doesNotThrow',
261-
message: `Got unwanted exception${details}\n${actual.message}`,
262-
stackStartFn: assert.doesNotThrow
263-
});
241+
if(actual===undefined){
242+
letdetails='';
243+
if(error&&error.name){
244+
details+=` (${error.name})`;
264245
}
246+
details+=message ? `: ${message}` : '.';
247+
innerFail({
248+
actual,
249+
expected: error,
250+
operator: 'throws',
251+
message: `Missing expected exception${details}`,
252+
stackStartFn: throws
253+
});
254+
}
255+
if(error&&expectedException(actual,error)===false){
265256
throwactual;
266257
}
267-
}
268-
269-
// Expected to throw an error.
270-
assert.throws=functionthrows(block,error,message){
271-
innerThrows(true,block,error,message);
272258
};
273259

274260
assert.doesNotThrow=functiondoesNotThrow(block,error,message){
275-
innerThrows(false,block,error,message);
261+
constactual=getActual(block);
262+
if(actual===undefined)
263+
return;
264+
265+
if(typeoferror==='string'){
266+
message=error;
267+
error=null;
268+
}
269+
270+
if(!error||expectedException(actual,error)){
271+
constdetails=message ? `: ${message}` : '.';
272+
innerFail({
273+
actual,
274+
expected: error,
275+
operator: 'doesNotThrow',
276+
message: `Got unwanted exception${details}\n${actual.message}`,
277+
stackStartFn: doesNotThrow
278+
});
279+
}
280+
throwactual;
276281
};
277282

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

‎test/parallel/test-assert.js‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,3 +773,14 @@ common.expectsError(
773773
message: 'null == true'
774774
}
775775
);
776+
777+
common.expectsError(
778+
// eslint-disable-next-line no-restricted-syntax
779+
()=>assert.throws(()=>{},'Error message','message'),
780+
{
781+
code: 'ERR_INVALID_ARG_TYPE',
782+
type: TypeError,
783+
message: 'The "error" argument must be one of type Function or RegExp. '+
784+
'Received type string'
785+
}
786+
);

0 commit comments

Comments
(0)