diff --git a/docs/api/assert/rejects.md b/docs/api/assert/rejects.md index ae056f3da..cf1047c38 100644 --- a/docs/api/assert/rejects.md +++ b/docs/api/assert/rejects.md @@ -11,33 +11,32 @@ version_added: "2.5.0" --- `rejects( promise, message = "" )`
-`rejects( promise, expectedMatcher, message = "" )`
+`rejects( promise, expected, message = "" )`
`rejectionValue = await rejects( promise, message = "" )` Test if the provided promise rejects, and optionally compare the rejection value. -| name | description | +| parameter | description | |------|-------------| -| `promise` (thenable) | Promise to test for rejection | -| `expectedMatcher` | Rejection value matcher | -| `message` (string) | Short description of the assertion | +| `promise` (thenable) | Promise to check for rejection | +| `expected` | Expected rejection or matcher | +| `message` (string) | Short description of the promise | -When testing code that is expected to return a rejected promise based on a -specific set of circumstances, use `assert.rejects()` for testing and -comparison. +Use `assert.rejects()` to test async functions that should throw, and any other function call that should return a promise that will reject. -The `expectedMatcher` argument can be: +The optional `expected` parameter can be one of these types (see [§ Examples](#examples)): -* A function that returns `true` when the assertion should be considered passing. -* An Error object. -* A base constructor, evaluated as `rejectionValue instanceof expectedMatcher`. -* A RegExp that matches (or partially matches) `rejectionValue.toString()`. +* Error object, to strictly compare the `message` and `name` properties, and check that the rejection value is an instance of this object's constructor. +* RegExp, to match (or partially match) the string representation. +* Error constructor, to confirm that the rejection value is an instance of this constructor. +* Custom validation function, to write your own logic returning `true` or `false`. -Note: in order to avoid confusion between the `message` and the `expectedMatcher`, the `expectedMatcher` **can not** be a string. +The returned promise from `assert.rejects()` is resolved with the rejection value (since QUnit 2.26). This can be used instead of an `expected` argument, to run other assertions against your your rejection value, such as [assert.propEqual()](./propEqual.md). -`assert.rejects()` returns a promise which is resolved with the rejection value from the provided -promise. This can be used instead of `expectedMatcher` to assert other facts about the expected -failure. +## Changelog + +| [QUnit 2.26](https://github.com/qunitjs/qunit/releases/tag/2.26.0) | Added the rejection value. +| [QUnit 2.5](https://github.com/qunitjs/qunit/releases/tag/2.5.0) | Introduced `assert.rejects()`. ## See also @@ -56,75 +55,92 @@ async function feedBaby (food) { } QUnit.test('example', async function (assert) { - assert.true(feedBaby('apple')); + assert.true(await feedBaby('apple')); + + await assert.rejects(feedBaby('sprouts')); await assert.rejects(feedBaby('sprouts'), RangeError); - assert.true(feedBaby('cucumber'), RangeError); + assert.true(await feedBaby('cucumber')); }); ``` ### Example: Matcher argument ```js +class CustomError { + constructor (message, code = 500) { + this.message = message; + this.code = code; + } + + getStatusCode () { + return this.code; + } +} + QUnit.test('rejects example', async function (assert) { - // simple check - assert.rejects(Promise.reject('some error')); + // default check + assert.rejects(Promise.reject('boo')); - // simple check + // default check assert.rejects( - Promise.reject('some error'), + Promise.reject('boo'), 'optional description here' ); - // match pattern on actual error + // Error object, evaluated as: + // - `err instance of Error` + // - strictly equal `err.message` + // - strictly equal `err.name` assert.rejects( - Promise.reject(new Error('some error')), - /some error/, - 'optional description here' + Promise.reject(new Error('boo')), + new Error('boo') ); - // Using a custom error constructor - function CustomError (message) { - this.message = message; - } - CustomError.prototype.toString = function () { - return this.message; - }; + // RegExp match against err.toString() + assert.rejects( + Promise.reject(new Error('My very specific error about something')), + /about something/ + ); - // actual error is an instance of the expected constructor assert.rejects( - Promise.reject(new CustomError('some error')), - CustomError + Promise.reject(new TypeError('Delta argument is required')), + /TypeError: Delta argument/ ); - // actual error has strictly equal `constructor`, `name` and `message` properties - // of the expected error object + // Error constructor, evaluated as `err instance of TypeError` assert.rejects( - Promise.reject(new CustomError('some error')), - new CustomError('some error') + Promise.reject(new TypeError('Delta argument is required')), + TypeError + ); + + // Error constructor: evaluated as `err instanceof CustomError` + assert.rejects( + Promise.reject(new CustomError('something')), + CustomError ); - // custom validation arrow function + // Custom validation arrow function assert.rejects( - Promise.reject(new CustomError('some error')), - (err) => err.toString() === 'some error' + Promise.reject(new CustomError('not found', 404)), + (err) => err.getStatusCode() === 404 ); - // custom validation function + // Custom validation function assert.rejects( - Promise.reject(new CustomError('some error')), + Promise.reject(new CustomError('not found', 404)), function (err) { - return err.toString() === 'some error'; + return err.getStatusCode() === 404; } ); - // custom validation using return value + // Custom validation via the return value const err = await assert.rejects( - Promise.reject(new CustomError('some error')) + Promise.reject(new CustomError('not found', 404)) ); assert.true(err instanceof CustomError); - assert.strictEqual(err.toString(), 'some error'); + assert.strictEqual(err.getStatusCode(), 404); }); ``` diff --git a/docs/api/assert/throws.md b/docs/api/assert/throws.md index bd4530214..93f66de54 100644 --- a/docs/api/assert/throws.md +++ b/docs/api/assert/throws.md @@ -12,34 +12,35 @@ version_added: "1.0.0" --- `throws( blockFn, message = "" )`
-`throws( blockFn, expectedMatcher, message = "" )`
+`throws( blockFn, expected, message = "" )`
`thrownValue = throws( blockFn, message = "" )` Test if a callback throws an exception, and optionally compare the thrown error. -| name | description | +| parameter | description | |------|-------------| | `blockFn` (function) | Function to execute | -| `expectedMatcher` | Expected error matcher | -| `message` (string) | Short description of the assertion | +| `expected` | Expected error or matcher | +| `message` (string) | Short description of the function | -When testing code that is expected to throw an exception based on a specific set of circumstances, use `assert.throws()` to catch the error object for testing and comparison. +Use `assert.throws()` when testing code that should throw an exception, to catch the error object and optionally match it against an expected error. -The `expectedMatcher` argument can be: +By default, `assert.throws()` passes if anything is thrown by the callback, and fails if the function runs to completion without throwing. -* An Error object. -* An Error constructor, evaluated as `errorValue instanceof expectedMatcher`. -* A RegExp that matches (or partially matches) the string representation. -* A callback with your own custom validation, that returns `true` or `false`. +The optional `expected` parameter can be one of these types (see [§ Examples](#examples)): -`assert.throws()` returns the value which was thrown from the provided -function. This can be used instead of `expectedMatcher` to assert other facts about the expected -failure. +* Error object, to strictly compare the `message` and `name` properties, and check that the thrown value is an instance of this object's constructor. +* RegExp, to match (or partially match) the string representation. +* Error constructor, to confirm that the thrown value is an instance of this constructor. +* Custom validation function, to write your own logic returning `true` or `false`. -

If you need to comply with classic ES3 syntax, such as in early versions of Closure Compiler, you can use `assert.raises()`, which is an alias for `assert.throws()`. It has the same signature and behaviour.

+`assert.throws()` returns the caught value back to your test (since QUnit 2.26). This can be used instead of an `expected` argument, to run other assertions against your caught value, such as [assert.propEqual()](./propEqual.md). + +

If you need to comply with classic ES3 syntax, such as in early versions of Closure Compiler, you can use **`assert.raises()`**, which is an alias for `assert.throws()` with the same signature and behaviour.

## Changelog +| [QUnit 2.26](https://github.com/qunitjs/qunit/releases/tag/2.26.0) | Added the return value. | [QUnit 2.12](https://github.com/qunitjs/qunit/releases/tag/2.12.0) | Added support for arrow functions as `expectedMatcher` callback function. | [QUnit 1.9](https://github.com/qunitjs/qunit/releases/tag/v1.9.0) | `assert.raises()` was renamed to `assert.throws()`.
The `assert.raises()` method remains supported as an alias. @@ -50,77 +51,96 @@ failure. ## Examples ```js +class CustomError { + constructor (message, code = 500) { + this.message = message; + this.code = code; + } + + getStatusCode () { + return this.code; + } +} + QUnit.test('throws example', function (assert) { - // simple check + // default check assert.throws(function () { throw new Error('boo'); }); - // simple check + // default check assert.throws( function () { throw new Error('boo'); }, - 'optional description here' + 'optional message here' ); - // match pattern on actual error + // Error object, evaluated as: + // - `err instance of Error` + // - strictly equal `err.message` + // - strictly equal `err.name` assert.throws( function () { - throw new Error('some error'); + throw new Error('boo'); }, - /some error/, - 'optional description here' + new Error('boo') ); - // using a custom error constructor - function CustomError (message) { - this.message = message; - } - CustomError.prototype.toString = function () { - return this.message; - }; + // RegExp match against err.toString() + assert.throws( + function () { + throw new Error('My very specific error about something'); + }, + /about something/ + ); - // actual error is an instance of the expected constructor assert.throws( function () { - throw new CustomError('some error'); + throw new TypeError('Delta argument is required'); }, - CustomError + /TypeError: Delta argument/ + ); + + // Error constructor, evaluated as `err instance of TypeError` + assert.throws( + function () { + throw new TypeError('Delta argument is required'); + }, + TypeError ); - // actual error has strictly equal `constructor`, `name` and `message` properties - // of the expected error object + // Error constructor: evaluated as `err instanceof CustomError` assert.throws( function () { - throw new CustomError('some error'); + throw new CustomError('something'); }, - new CustomError('some error') + CustomError ); - // custom validation arrow function + // Custom validation arrow function assert.throws( function () { - throw new CustomError('some error'); + throw new CustomError('not found', 404); }, - (err) => err.toString() === 'some error' + (err) => err.getStatusCode() === 404 ); - // custom validation function + // Custom validation function assert.throws( function () { - throw new CustomError('some error'); + throw new CustomError('not found', 404); }, function (err) { - return err.toString() === 'some error'; + return err.getStatusCode() === 404; } ); - // custom validation using return value + // Custom validation via the return value const err = assert.throws(function () { - throw new CustomError('some error'); + throw new CustomError('not found', 404); }); assert.true(err instanceof CustomError); - assert.strictEqual(err.toString(), 'some error'); + assert.strictEqual(err.getStatusCode(), 404); }); ``` diff --git a/src/core/assert.js b/src/core/assert.js index 4bb342457..63b6ab7e7 100644 --- a/src/core/assert.js +++ b/src/core/assert.js @@ -374,6 +374,8 @@ function validateExpectedExceptionArgs (expected, message, assertionMethod) { expected = undefined; return [expected, message]; } else { + // To avoid ambiguity between the `message` and the `expected` argument, + // we cannot support a string as expected value in assert.throws() and assert.rejects(). throw new Error( 'assert.' + assertionMethod + ' does not accept a string value for the expected argument.\n' + 'Use a non-string object value (e.g. RegExp or validator function) instead if necessary.'