Skip to content

Commit ebd60fa

Browse files
BridgeARMylesBorins
authored andcommitted
assert: .throws accept objects
From now on it is possible to use a validation object in throws instead of the other possibilites. Backport-PR-URL: #19230 PR-URL: #17584 Refs: #17557 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Ron Korving <[email protected]> Reviewed-By: Yuta Hiroto <[email protected]>
1 parent 7457093 commit ebd60fa

File tree

3 files changed

+138
-17
lines changed

3 files changed

+138
-17
lines changed

‎doc/api/assert.md‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -709,18 +709,21 @@ instead of the `AssertionError`.
709709
<!-- YAML
710710
added: v0.1.21
711711
changes:
712+
- version: REPLACEME
713+
pr-url: https://github.com/nodejs/node/pull/REPLACEME
714+
description: The `error` parameter can now be an object as well.
712715
- version: v4.2.0
713716
pr-url: https://github.com/nodejs/node/pull/3276
714717
description: The `error` parameter can now be an arrow function.
715718
-->
716719
*`block`{Function}
717-
*`error`{RegExp|Function}
720+
*`error`{RegExp|Function|object}
718721
*`message`{any}
719722

720723
Expects the function `block` to throw an error.
721724

722-
If specified, `error` can be a constructor, [`RegExp`][], or validation
723-
function.
725+
If specified, `error` can be a constructor, [`RegExp`][], a validation
726+
function, or an object where each property will be tested for.
724727

725728
If specified, `message` will be the message provided by the `AssertionError` if
726729
the block fails to throw.
@@ -766,6 +769,23 @@ assert.throws(
766769
);
767770
```
768771

772+
Custom error object / error instance:
773+
774+
```js
775+
assert.throws(
776+
() =>{
777+
consterr=newTypeError('Wrong value');
778+
err.code=404;
779+
throw err;
780+
},
781+
{
782+
name:'TypeError',
783+
message:'Wrong value'
784+
// Note that only properties on the error object will be tested!
785+
}
786+
);
787+
```
788+
769789
Note that `error` can not be a string. If a string is provided as the second
770790
argument, then `error` is assumed to be omitted and the string will be used for
771791
`message` instead. This can lead to easy-to-miss mistakes. Please read the

‎lib/assert.js‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
const{ isDeepEqual, isDeepStrictEqual }=
2424
require('internal/util/comparisons');
2525
consterrors=require('internal/errors');
26+
const{ inspect }=require('util');
2627

2728
// The assert module provides functions that throw
2829
// AssertionError's when particular conditions are not met. The
@@ -196,10 +197,44 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message){
196197
}
197198
};
198199

199-
functionexpectedException(actual,expected){
200+
functioncreateMsg(msg,key,actual,expected){
201+
if(msg)
202+
returnmsg;
203+
return`${key}: expected ${inspect(expected[key])}, `+
204+
`not ${inspect(actual[key])}`;
205+
}
206+
207+
functionexpectedException(actual,expected,msg){
200208
if(typeofexpected!=='function'){
201-
// Should be a RegExp, if not fail hard
202-
returnexpected.test(actual);
209+
if(expectedinstanceofRegExp)
210+
returnexpected.test(actual);
211+
// assert.doesNotThrow does not accept objects.
212+
if(arguments.length===2){
213+
thrownewerrors.TypeError('ERR_INVALID_ARG_TYPE','expected',
214+
['Function','RegExp'],expected);
215+
}
216+
// The name and message could be non enumerable. Therefore test them
217+
// explicitly.
218+
if('name'inexpected){
219+
assert.strictEqual(
220+
actual.name,
221+
expected.name,
222+
createMsg(msg,'name',actual,expected));
223+
}
224+
if('message'inexpected){
225+
assert.strictEqual(
226+
actual.message,
227+
expected.message,
228+
createMsg(msg,'message',actual,expected));
229+
}
230+
constkeys=Object.keys(expected);
231+
for(constkeyofkeys){
232+
assert.deepStrictEqual(
233+
actual[key],
234+
expected[key],
235+
createMsg(msg,key,actual,expected));
236+
}
237+
returntrue;
203238
}
204239
// Guard instanceof against arrow functions as they don't have a prototype.
205240
if(expected.prototype!==undefined&&actualinstanceofexpected){
@@ -252,7 +287,7 @@ assert.throws = function throws(block, error, message){
252287
stackStartFn: throws
253288
});
254289
}
255-
if(error&&expectedException(actual,error)===false){
290+
if(error&&expectedException(actual,error,message)===false){
256291
throwactual;
257292
}
258293
};

‎test/parallel/test-assert.js‎

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,6 @@ assert.ok(a.AssertionError.prototype instanceof Error,
3939

4040
assert.throws(makeBlock(a,false),a.AssertionError,'ok(false)');
4141

42-
// Using a object as second arg results in a failure
43-
assert.throws(
44-
()=>{assert.throws(()=>{thrownewError();},{foo: 'bar'});},
45-
common.expectsError({
46-
type: TypeError,
47-
message: 'expected.test is not a function'
48-
})
49-
);
50-
51-
5242
assert.doesNotThrow(makeBlock(a,true),a.AssertionError,'ok(true)');
5343

5444
assert.doesNotThrow(makeBlock(a,'test','ok(\'test\')'));
@@ -784,3 +774,79 @@ common.expectsError(
784774
'Received type string'
785775
}
786776
);
777+
778+
{
779+
consterrFn=()=>{
780+
consterr=newTypeError('Wrong value');
781+
err.code=404;
782+
throwerr;
783+
};
784+
consterrObj={
785+
name: 'TypeError',
786+
message: 'Wrong value'
787+
};
788+
assert.throws(errFn,errObj);
789+
790+
errObj.code=404;
791+
assert.throws(errFn,errObj);
792+
793+
errObj.code='404';
794+
common.expectsError(
795+
// eslint-disable-next-line no-restricted-syntax
796+
()=>assert.throws(errFn,errObj),
797+
{
798+
code: 'ERR_ASSERTION',
799+
type: assert.AssertionError,
800+
message: 'code: expected \'404\', not 404'
801+
}
802+
);
803+
804+
errObj.code=404;
805+
errObj.foo='bar';
806+
common.expectsError(
807+
// eslint-disable-next-line no-restricted-syntax
808+
()=>assert.throws(errFn,errObj),
809+
{
810+
code: 'ERR_ASSERTION',
811+
type: assert.AssertionError,
812+
message: 'foo: expected \'bar\', not undefined'
813+
}
814+
);
815+
816+
common.expectsError(
817+
()=>assert.throws(()=>{thrownewError();},{foo: 'bar'},'foobar'),
818+
{
819+
type: assert.AssertionError,
820+
code: 'ERR_ASSERTION',
821+
message: 'foobar'
822+
}
823+
);
824+
825+
common.expectsError(
826+
()=>assert.doesNotThrow(()=>{thrownewError();},{foo: 'bar'}),
827+
{
828+
type: TypeError,
829+
code: 'ERR_INVALID_ARG_TYPE',
830+
message: 'The "expected" argument must be one of type Function or '+
831+
'RegExp. Received type object'
832+
}
833+
);
834+
835+
assert.throws(()=>{thrownewError('e');},newError('e'));
836+
common.expectsError(
837+
()=>assert.throws(()=>{thrownewTypeError('e');},newError('e')),
838+
{
839+
type: assert.AssertionError,
840+
code: 'ERR_ASSERTION',
841+
message: "name: expected 'Error', not 'TypeError'"
842+
}
843+
);
844+
common.expectsError(
845+
()=>assert.throws(()=>{thrownewError('foo');},newError('')),
846+
{
847+
type: assert.AssertionError,
848+
code: 'ERR_ASSERTION',
849+
message: "message: expected '', not 'foo'"
850+
}
851+
);
852+
}

0 commit comments

Comments
(0)