Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assert: add strict functionality export #17002

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
192 changes: 137 additions & 55 deletions doc/api/assert.md
Expand Up @@ -7,9 +7,57 @@
The `assert` module provides a simple set of assertion tests that can be used to
test invariants.

A `strict` and a `legacy` mode exist, while it is recommended to only use
[`strict mode`][].

For more information about the used equality comparisons see
[MDN's guide on equality comparisons and sameness][mdn-equality-guide].

## Strict mode
<!-- YAML
added: REPLACEME
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/17002
description: Added strict mode to the assert module.
-->

When using the `strict mode`, any `assert` function will use the equality used in
the strict function mode. So [`assert.deepEqual()`][] will, for example, work the
same as [`assert.deepStrictEqual()`][].

It can be accessed using:

```js
const assert = require('assert').strict;
```

## Legacy mode

> Stability: 0 - Deprecated: Use strict mode instead.

When accessing `assert` directly instead of using the `strict` property, the
[Abstract Equality Comparison][] will be used for any function without a
"strict" in its name (e.g. [`assert.deepEqual()`][]).

It can be accessed using:

```js
const assert = require('assert');
```

It is recommended to use the [`strict mode`][] instead as the
[Abstract Equality Comparison][] can often have surprising results. Especially
in case of [`assert.deepEqual()`][] as the used comparison rules there are very
lax.

E.g.

```js
// WARNING: This does not throw an AssertionError!
assert.deepEqual(/a/gi, new Date());
```

## assert(value[, message])
<!-- YAML
added: v0.5.9
Expand Down Expand Up @@ -43,6 +91,14 @@ changes:
* `expected` {any}
* `message` {any}

**Strict mode**

An alias of [`assert.deepStrictEqual()`][].

**Legacy mode**

> Stability: 0 - Deprecated: Use [`assert.deepStrictEqual()`][] instead.

Tests for deep equality between the `actual` and `expected` parameters.
Primitive values are compared with the [Abstract Equality Comparison][]
( `==` ).
Expand Down Expand Up @@ -104,7 +160,7 @@ assert.deepEqual(obj1, obj4);
If the values are not equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.deepStrictEqual(actual, expected[, message])
Expand Down Expand Up @@ -137,50 +193,50 @@ changes:
* `expected` {any}
* `message` {any}

Identical to [`assert.deepEqual()`][] with the following exceptions:

1. Primitive values besides `NaN` are compared using the [Strict Equality
Comparison][] ( `===` ). Set and Map values, Map keys and `NaN` are compared
using the [SameValueZero][] comparison (which means they are free of the
[caveats][]).
2. [`[[Prototype]]`][prototype-spec] of objects are compared using
the [Strict Equality Comparison][] too.
3. [Type tags][Object.prototype.toString()] of objects should be the same.
4. [Object wrappers][] are compared both as objects and unwrapped values.
5. `0` and `-0` are not considered equal.
6. Enumerable own [`Symbol`][] properties are compared as well.
Tests for deep equality between the `actual` and `expected` parameters.
"Deep" equality means that the enumerable "own" properties of child objects
are recursively evaluated also by the following rules.

### Comparison details

* Primitive values are compared using the [SameValue Comparison][], used by
[`Object.is()`][].
* [Type tags][Object.prototype.toString()] of objects should be the same.
* [`[[Prototype]]`][prototype-spec] of objects are compared using
the [Strict Equality Comparison][].
* Only [enumerable "own" properties][] are considered.
* [`Error`][] names and messages are always compared, even if these are not
enumerable properties.
* Enumerable own [`Symbol`][] properties are compared as well.
* [Object wrappers][] are compared both as objects and unwrapped values.
* Object properties are compared unordered.
* Map keys and Set items are compared unordered.
* Recursion stops when both sides differ or both sides encounter a circular
reference.

```js
const assert = require('assert');

assert.deepEqual({ a: 1 }, { a: '1' });
// OK, because 1 == '1'
const assert = require('assert').strict;

assert.deepStrictEqual({ a: 1 }, { a: '1' });
// AssertionError: { a: 1 } deepStrictEqual { a: '1' }
// because 1 !== '1' using strict equality
// because 1 !== '1' using SameValue comparison

// The following objects don't have own properties
const date = new Date();
const object = {};
const fakeDate = {};

Object.setPrototypeOf(fakeDate, Date.prototype);

assert.deepEqual(object, fakeDate);
// OK, doesn't check [[Prototype]]
assert.deepStrictEqual(object, fakeDate);
// AssertionError: {} deepStrictEqual Date {}
// Different [[Prototype]]

assert.deepEqual(date, fakeDate);
// OK, doesn't check type tags
assert.deepStrictEqual(date, fakeDate);
// AssertionError: 2017-03-11T14:25:31.849Z deepStrictEqual Date {}
// Different type tags

assert.deepStrictEqual(NaN, NaN);
// OK, because of the SameValueZero comparison
// OK, because of the SameValue comparison

assert.deepStrictEqual(new Number(1), new Number(2));
// Fails because the wrapped number is unwrapped and compared as well.
Expand All @@ -203,7 +259,7 @@ assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 });
If the values are not equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.doesNotThrow(block[, error][, message])
Expand Down Expand Up @@ -279,6 +335,14 @@ added: v0.1.21
* `expected` {any}
* `message` {any}

**Strict mode**

An alias of [`assert.strictEqual()`][].

**Legacy mode**

> Stability: 0 - Deprecated: Use [`assert.strictEqual()`][] instead.

Tests shallow, coercive equality between the `actual` and `expected` parameters
using the [Abstract Equality Comparison][] ( `==` ).

Expand All @@ -299,7 +363,7 @@ assert.equal({ a: { b: 1 } }, { a: { b: 1 } });
If the values are not equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.fail([message])
Expand All @@ -315,7 +379,7 @@ added: v0.1.21

Throws an `AssertionError`. If `message` is falsy, the error message is set as
the values of `actual` and `expected` separated by the provided `operator`. If
the `message` parameter is an instance of an `Error` then it will be thrown
the `message` parameter is an instance of an [`Error`][] then it will be thrown
instead of the `AssertionError`. If just the two `actual` and `expected`
arguments are provided, `operator` will default to `'!='`. If `message` is
provided only it will be used as the error message, the other arguments will be
Expand All @@ -325,7 +389,7 @@ all stack frames above that function will be removed from stacktrace (see
`Failed` will be used.

```js
const assert = require('assert');
const assert = require('assert').strict;

assert.fail(1, 2, undefined, '>');
// AssertionError [ERR_ASSERTION]: 1 > 2
Expand Down Expand Up @@ -376,7 +440,7 @@ Throws `value` if `value` is truthy. This is useful when testing the `error`
argument in callbacks.

```js
const assert = require('assert');
const assert = require('assert').strict;

assert.ifError(0);
// OK
Expand Down Expand Up @@ -412,6 +476,14 @@ changes:
* `expected` {any}
* `message` {any}

**Strict mode**

An alias of [`assert.notDeepStrictEqual()`][].

**Legacy mode**

> Stability: 0 - Deprecated: Use [`assert.notDeepStrictEqual()`][] instead.

Tests for any deep inequality. Opposite of [`assert.deepEqual()`][].

```js
Expand Down Expand Up @@ -450,7 +522,7 @@ assert.notDeepEqual(obj1, obj4);
If the values are deeply equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.notDeepStrictEqual(actual, expected[, message])
Expand Down Expand Up @@ -486,10 +558,7 @@ changes:
Tests for deep strict inequality. Opposite of [`assert.deepStrictEqual()`][].

```js
const assert = require('assert');

assert.notDeepEqual({ a: 1 }, { a: '1' });
// AssertionError: { a: 1 } notDeepEqual { a: '1' }
const assert = require('assert').strict;

assert.notDeepStrictEqual({ a: 1 }, { a: '1' });
// OK
Expand All @@ -498,8 +567,8 @@ assert.notDeepStrictEqual({ a: 1 }, { a: '1' });
If the values are deeply and strictly equal, an `AssertionError` is thrown with
a `message` property set equal to the value of the `message` parameter. If the
`message` parameter is undefined, a default error message is assigned. If the
`message` parameter is an instance of an `Error` then it will be thrown instead
of the `AssertionError`.
`message` parameter is an instance of an [`Error`][] then it will be thrown
instead of the `AssertionError`.

## assert.notEqual(actual, expected[, message])
<!-- YAML
Expand All @@ -509,6 +578,14 @@ added: v0.1.21
* `expected` {any}
* `message` {any}

**Strict mode**

An alias of [`assert.notStrictEqual()`][].

**Legacy mode**

> Stability: 0 - Deprecated: Use [`assert.notStrictEqual()`][] instead.

Tests shallow, coercive inequality with the [Abstract Equality Comparison][]
( `!=` ).

Expand All @@ -525,10 +602,10 @@ assert.notEqual(1, '1');
// AssertionError: 1 != '1'
```

If the values are equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
If the values are equal, an `AssertionError` is thrown with a `message` property
set equal to the value of the `message` parameter. If the `message` parameter is
undefined, a default error message is assigned. If the `message` parameter is an
instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.notStrictEqual(actual, expected[, message])
Expand All @@ -543,10 +620,11 @@ changes:
* `expected` {any}
* `message` {any}

Tests equality determined by the [`Object.is()`][] comparison.
Tests strict inequality between the `actual` and `expected` parameters as
determined by the [SameValue Comparison][].

```js
const assert = require('assert');
const assert = require('assert').strict;

assert.notStrictEqual(1, 2);
// OK
Expand All @@ -558,11 +636,11 @@ assert.notStrictEqual(1, '1');
// OK
```

If the values are strictly equal, an `AssertionError` is thrown with a
`message` property set equal to the value of the `message` parameter. If the
`message` parameter is undefined, a default error message is assigned. If the
`message` parameter is an instance of an `Error` then it will be thrown instead
of the `AssertionError`.
If the values are strictly equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.ok(value[, message])
<!-- YAML
Expand All @@ -577,11 +655,11 @@ Tests if `value` is truthy. It is equivalent to
If `value` is not truthy, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is `undefined`, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

```js
const assert = require('assert');
const assert = require('assert').strict;

assert.ok(true);
// OK
Expand All @@ -607,10 +685,11 @@ changes:
* `expected` {any}
* `message` {any}

Tests equality determined by the [`Object.is()`][] comparison.
Tests strict equality between the `actual` and `expected` parameters as
determined by the [SameValue Comparison][].

```js
const assert = require('assert');
const assert = require('assert').strict;

assert.strictEqual(1, 2);
// AssertionError: 1 strictEqual 2
Expand All @@ -625,8 +704,8 @@ assert.strictEqual(1, '1');
If the values are not strictly equal, an `AssertionError` is thrown with a
`message` property set equal to the value of the `message` parameter. If the
`message` parameter is undefined, a default error message is assigned. If the
`message` parameter is an instance of an `Error` then it will be thrown instead
of the `AssertionError`.
`message` parameter is an instance of an [`Error`][] then it will be thrown
instead of the `AssertionError`.

## assert.throws(block[, error][, message])
<!-- YAML
Expand Down Expand Up @@ -708,13 +787,16 @@ assert.throws(myFunction, /missing foo/, 'did not throw with expected message');
[`TypeError`]: errors.html#errors_class_typeerror
[`assert.deepEqual()`]: #assert_assert_deepequal_actual_expected_message
[`assert.deepStrictEqual()`]: #assert_assert_deepstrictequal_actual_expected_message
[`assert.notDeepStrictEqual()`]: #assert_assert_notdeepstrictequal_actual_expected_message
[`assert.notStrictEqual()`]: #assert_assert_notstrictequal_actual_expected_message
[`assert.ok()`]: #assert_assert_ok_value_message
[`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message
[`assert.throws()`]: #assert_assert_throws_block_error_message
[`strict mode`]: #assert_strict_mode
[Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison
[Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring
[SameValueZero]: https://tc39.github.io/ecma262/#sec-samevaluezero
[SameValue Comparison]: https://tc39.github.io/ecma262/#sec-samevalue
[Strict Equality Comparison]: https://tc39.github.io/ecma262/#sec-strict-equality-comparison
[caveats]: #assert_caveats
[enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
[mdn-equality-guide]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
[prototype-spec]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
Expand Down
9 changes: 9 additions & 0 deletions doc/api/deprecations.md
Expand Up @@ -799,6 +799,15 @@ Type: End-of-Life
cause a lot of issues. See https://github.com/nodejs/node/issues/14328 for more
details.

<a id="DEP0XXX"></a>
### DEP0XXX: require('assert')

Type: Documentation-only

Importing assert directly is not recommended as the exposed functions will use
loose equality checks. Use `require('assert').strict` instead. The API is the
same as the legacy assert but it will always use strict equality checks.

[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
[`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array
[`Buffer.from(buffer)`]: buffer.html#buffer_class_method_buffer_from_buffer
Expand Down