Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 66 additions & 50 deletions docs/api/assert/rejects.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,32 @@ version_added: "2.5.0"
---

`rejects( promise, message = "" )`<br>
`rejects( promise, expectedMatcher, message = "" )`<br>
`rejects( promise, expected, message = "" )`<br>
`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

Expand All @@ -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);
});
```

Expand Down
108 changes: 64 additions & 44 deletions docs/api/assert/throws.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,35 @@ version_added: "1.0.0"
---

`throws( blockFn, message = "" )`<br>
`throws( blockFn, expectedMatcher, message = "" )`<br>
`throws( blockFn, expected, message = "" )`<br>
`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`.

<p class="note" markdown="1">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.</p>
`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).

<p class="note" markdown="1">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.</p>

## 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()`.<br>The `assert.raises()` method remains supported as an alias.

Expand All @@ -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);
});
```
2 changes: 2 additions & 0 deletions src/core/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
Expand Down
Loading