Assert (断言)#

稳定性:2 - 稳定

源代码: lib/assert.js

node:assert 模块提供了一组用于验证不变量的断言函数。

严格断言模式#

在严格断言模式下,非严格方法的行为与其对应的严格方法类似。例如,assert.deepEqual() 的行为将类似于 assert.deepStrictEqual()

在严格断言模式下,对象的错误消息会显示差异(diff)。在旧式断言模式下,对象的错误消息会显示对象本身,且常常被截断。

要使用严格断言模式

import { strict as assert } from 'node:assert';const assert = require('node:assert').strict;
import assert from 'node:assert/strict';const assert = require('node:assert/strict');

错误差异示例

import { strict as assert } from 'node:assert';

assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected ... Lines skipped
//
//   [
//     [
// ...
//       2,
// +     3
// -     '3'
//     ],
// ...
//     5
//   ]const assert = require('node:assert/strict');

assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected ... Lines skipped
//
//   [
//     [
// ...
//       2,
// +     3
// -     '3'
//     ],
// ...
//     5
//   ]

要禁用颜色,请使用 NO_COLORNODE_DISABLE_COLORS 环境变量。这也会禁用 REPL 中的颜色。有关终端环境中颜色支持的更多信息,请阅读 tty getColorDepth() 文档。

旧式断言模式#

旧式断言模式在以下函数中使用== 运算符

要使用旧式断言模式

import assert from 'node:assert';const assert = require('node:assert');

旧式断言模式可能会产生令人意外的结果,尤其是在使用 assert.deepEqual() 时。

// WARNING: This does not throw an AssertionError in legacy assertion mode!
assert.deepEqual(/a/gi, new Date()); 

类: assert.AssertionError#

表示断言失败。所有由 node:assert 模块抛出的错误都将是 AssertionError 类的实例。

new assert.AssertionError(options)#

  • options <Object>
    • message <string> 如果提供,错误消息将设置为此值。
    • actual <any> 错误实例上的 actual 属性。
    • expected <any> 错误实例上的 expected 属性。
    • operator <string> 错误实例上的 operator 属性。
    • stackStartFn <Function> 如果提供,生成的堆栈跟踪将省略此函数之前的帧。
    • diff <string> 如果设置为 'full',则在断言错误中显示完整的差异。默认为 'simple'。接受的值:'simple''full'

一个 <Error> 的子类,表示断言失败。

所有实例都包含内置的 Error 属性(messagename)以及:

  • actual <any> 设置为诸如 assert.strictEqual() 等方法的 actual 参数。
  • expected <any> 设置为诸如 assert.strictEqual() 等方法的 expected 值。
  • generatedMessage <boolean> 指示消息是自动生成的(true)还是手动提供的。
  • code <string> 值始终为 ERR_ASSERTION,以表明该错误是断言错误。
  • operator <string> 设置为传入的运算符值。
import assert from 'node:assert';

// Generate an AssertionError to compare the error message later:
const { message } = new assert.AssertionError({
  actual: 1,
  expected: 2,
  operator: 'strictEqual',
});

// Verify error output:
try {
  assert.strictEqual(1, 2);
} catch (err) {
  assert(err instanceof assert.AssertionError);
  assert.strictEqual(err.message, message);
  assert.strictEqual(err.name, 'AssertionError');
  assert.strictEqual(err.actual, 1);
  assert.strictEqual(err.expected, 2);
  assert.strictEqual(err.code, 'ERR_ASSERTION');
  assert.strictEqual(err.operator, 'strictEqual');
  assert.strictEqual(err.generatedMessage, true);
}const assert = require('node:assert');

// Generate an AssertionError to compare the error message later:
const { message } = new assert.AssertionError({
  actual: 1,
  expected: 2,
  operator: 'strictEqual',
});

// Verify error output:
try {
  assert.strictEqual(1, 2);
} catch (err) {
  assert(err instanceof assert.AssertionError);
  assert.strictEqual(err.message, message);
  assert.strictEqual(err.name, 'AssertionError');
  assert.strictEqual(err.actual, 1);
  assert.strictEqual(err.expected, 2);
  assert.strictEqual(err.code, 'ERR_ASSERTION');
  assert.strictEqual(err.operator, 'strictEqual');
  assert.strictEqual(err.generatedMessage, true);
}

类: assert.Assert#

Assert 类允许创建具有自定义选项的独立断言实例。

new assert.Assert([options])#

  • options <Object>
    • diff <string> 如果设置为 'full',则在断言错误中显示完整的差异。默认为 'simple'。接受的值:'simple''full'
    • strict <boolean> 如果设置为 true,非严格方法的行为将与其对应的严格方法类似。默认为 true
    • skipPrototype <boolean> 如果设置为 true,则在深度相等检查中跳过原型和构造函数的比较。默认为 false

创建一个新的断言实例。diff 选项控制断言错误消息中差异的详细程度。

const { Assert } = require('node:assert');
const assertInstance = new Assert({ diff: 'full' });
assertInstance.deepStrictEqual({ a: 1 }, { a: 2 });
// Shows a full diff in the error message. 

重要提示:当从 Assert 实例中解构断言方法时,这些方法会失去与该实例配置选项(如 diffstrictskipPrototype 设置)的连接。解构后的方法将回退到默认行为。

const myAssert = new Assert({ diff: 'full' });

// This works as expected - uses 'full' diff
myAssert.strictEqual({ a: 1 }, { b: { c: 1 } });

// This loses the 'full' diff setting - falls back to default 'simple' diff
const { strictEqual } = myAssert;
strictEqual({ a: 1 }, { b: { c: 1 } }); 

skipPrototype 选项会影响所有深度相等方法。

class Foo {
  constructor(a) {
    this.a = a;
  }
}

class Bar {
  constructor(a) {
    this.a = a;
  }
}

const foo = new Foo(1);
const bar = new Bar(1);

// Default behavior - fails due to different constructors
const assert1 = new Assert();
assert1.deepStrictEqual(foo, bar); // AssertionError

// Skip prototype comparison - passes if properties are equal
const assert2 = new Assert({ skipPrototype: true });
assert2.deepStrictEqual(foo, bar); // OK 

解构后,方法将无法访问实例的 this 上下文,并恢复为默认的断言行为(diff: 'simple',非严格模式)。要在使用解构方法时保留自定义选项,请避免解构并直接在实例上调用方法。

assert(value[, message])#

assert.ok() 的别名。

assert.deepEqual(actual, expected[, message])#

严格断言模式

assert.deepStrictEqual() 的别名。

旧式断言模式

稳定性: 3 - 旧式: 请改用 assert.deepStrictEqual()

测试 actualexpected 参数之间的深度相等性。考虑改用 assert.deepStrictEqual()assert.deepEqual() 可能会产生令人意外的结果。

深度相等意味着子对象的可枚举“自有”属性也按照以下规则进行递归评估。

比较详情#

  • 原始值使用== 运算符进行比较,但 <NaN> 除外。如果两侧都是 <NaN>,则视为相同。
  • 对象的类型标签应该相同。
  • 只考虑可枚举的“自有”属性
  • 始终比较 <Error> 的名称、消息、原因(cause)和错误(errors),即使这些属性是不可枚举的。
  • 对象包装器会同时作为对象和解包后的值进行比较。
  • Object 属性以无序方式进行比较。
  • <Map> 的键和 <Set> 的项以无序方式进行比较。
  • 当两侧不同或任一侧遇到循环引用时,递归停止。
  • 实现不测试对象的[[Prototype]]
  • 不比较 <Symbol> 属性。
  • <WeakMap><WeakSet><Promise> 实例进行结构化比较。它们仅在引用同一个对象时才相等。任何不同的 WeakMapWeakSetPromise 实例之间的比较都会导致不相等,即使它们包含相同的内容。
  • 总是比较 <RegExp> 的 lastIndex、flags 和 source,即使这些属性是不可枚举的。

以下示例不会抛出 AssertionError,因为原始值是使用== 运算符进行比较的。

import assert from 'node:assert';
// WARNING: This does not throw an AssertionError!

assert.deepEqual('+00000000', false);const assert = require('node:assert');
// WARNING: This does not throw an AssertionError!

assert.deepEqual('+00000000', false);

“深度”相等意味着子对象的可枚举“自有”属性也会被评估。

import assert from 'node:assert';

const obj1 = {
  a: {
    b: 1,
  },
};
const obj2 = {
  a: {
    b: 2,
  },
};
const obj3 = {
  a: {
    b: 1,
  },
};
const obj4 = { __proto__: obj1 };

assert.deepEqual(obj1, obj1);
// OK

// Values of b are different:
assert.deepEqual(obj1, obj2);
// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } }

assert.deepEqual(obj1, obj3);
// OK

// Prototypes are ignored:
assert.deepEqual(obj1, obj4);
// AssertionError: { a: { b: 1 } } deepEqual {}const assert = require('node:assert');

const obj1 = {
  a: {
    b: 1,
  },
};
const obj2 = {
  a: {
    b: 2,
  },
};
const obj3 = {
  a: {
    b: 1,
  },
};
const obj4 = { __proto__: obj1 };

assert.deepEqual(obj1, obj1);
// OK

// Values of b are different:
assert.deepEqual(obj1, obj2);
// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } }

assert.deepEqual(obj1, obj3);
// OK

// Prototypes are ignored:
assert.deepEqual(obj1, obj4);
// AssertionError: { a: { b: 1 } } deepEqual {}

如果值不相等,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.deepStrictEqual(actual, expected[, message])#

测试 actualexpected 参数之间的深度严格相等性。“深度”相等意味着子对象的可枚举“自有”属性也按照以下规则进行递归评估。

比较详情#

  • 原始值使用 Object.is() 进行比较。
  • 对象的类型标签应该相同。
  • 对象的 [[Prototype]] 使用=== 运算符进行比较。
  • 只考虑可枚举的“自有”属性
  • 总是比较 <Error> 的名称、消息、原因(cause)和错误(errors),即使这些属性不是可枚举的。也会比较 errors
  • 也会比较可枚举的自有 <Symbol> 属性。
  • 对象包装器会同时作为对象和解包后的值进行比较。
  • Object 属性以无序方式进行比较。
  • <Map> 的键和 <Set> 的项以无序方式进行比较。
  • 当两侧不同或任一侧遇到循环引用时,递归停止。
  • <WeakMap><WeakSet><Promise> 实例进行结构化比较。它们仅在引用同一个对象时才相等。任何不同的 WeakMapWeakSetPromise 实例之间的比较都会导致不相等,即使它们包含相同的内容。
  • 总是比较 <RegExp> 的 lastIndex、flags 和 source,即使这些属性是不可枚举的。
import assert from 'node:assert/strict';

// This fails because 1 !== '1'.
assert.deepStrictEqual({ a: 1 }, { a: '1' });
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
//   {
// +   a: 1
// -   a: '1'
//   }

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

// Different [[Prototype]]:
assert.deepStrictEqual(object, fakeDate);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + {}
// - Date {}

// Different type tags:
assert.deepStrictEqual(date, fakeDate);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + 2018-04-26T00:49:08.604Z
// - Date {}

assert.deepStrictEqual(NaN, NaN);
// OK because Object.is(NaN, NaN) is true.

// Different unwrapped numbers:
assert.deepStrictEqual(new Number(1), new Number(2));
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + [Number: 1]
// - [Number: 2]

assert.deepStrictEqual(new String('foo'), Object('foo'));
// OK because the object and the string are identical when unwrapped.

assert.deepStrictEqual(-0, -0);
// OK

// Different zeros:
assert.deepStrictEqual(0, -0);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + 0
// - -0

const symbol1 = Symbol();
const symbol2 = Symbol();
assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol1]: 1 });
// OK, because it is the same symbol on both objects.

assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 });
// AssertionError [ERR_ASSERTION]: Inputs identical but not reference equal:
//
// {
//   Symbol(): 1
// }

const weakMap1 = new WeakMap();
const weakMap2 = new WeakMap();
const obj = {};

weakMap1.set(obj, 'value');
weakMap2.set(obj, 'value');

// Comparing different instances fails, even with same contents
assert.deepStrictEqual(weakMap1, weakMap2);
// AssertionError: Values have same structure but are not reference-equal:
//
// WeakMap {
//   <items unknown>
// }

// Comparing the same instance to itself succeeds
assert.deepStrictEqual(weakMap1, weakMap1);
// OK

const weakSet1 = new WeakSet();
const weakSet2 = new WeakSet();
weakSet1.add(obj);
weakSet2.add(obj);

// Comparing different instances fails, even with same contents
assert.deepStrictEqual(weakSet1, weakSet2);
// AssertionError: Values have same structure but are not reference-equal:
// + actual - expected
//
// WeakSet {
//   <items unknown>
// }

// Comparing the same instance to itself succeeds
assert.deepStrictEqual(weakSet1, weakSet1);
// OKconst assert = require('node:assert/strict');

// This fails because 1 !== '1'.
assert.deepStrictEqual({ a: 1 }, { a: '1' });
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
//   {
// +   a: 1
// -   a: '1'
//   }

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

// Different [[Prototype]]:
assert.deepStrictEqual(object, fakeDate);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + {}
// - Date {}

// Different type tags:
assert.deepStrictEqual(date, fakeDate);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + 2018-04-26T00:49:08.604Z
// - Date {}

assert.deepStrictEqual(NaN, NaN);
// OK because Object.is(NaN, NaN) is true.

// Different unwrapped numbers:
assert.deepStrictEqual(new Number(1), new Number(2));
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + [Number: 1]
// - [Number: 2]

assert.deepStrictEqual(new String('foo'), Object('foo'));
// OK because the object and the string are identical when unwrapped.

assert.deepStrictEqual(-0, -0);
// OK

// Different zeros:
assert.deepStrictEqual(0, -0);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// + 0
// - -0

const symbol1 = Symbol();
const symbol2 = Symbol();
assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol1]: 1 });
// OK, because it is the same symbol on both objects.

assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 });
// AssertionError [ERR_ASSERTION]: Inputs identical but not reference equal:
//
// {
//   Symbol(): 1
// }

const weakMap1 = new WeakMap();
const weakMap2 = new WeakMap();
const obj = {};

weakMap1.set(obj, 'value');
weakMap2.set(obj, 'value');

// Comparing different instances fails, even with same contents
assert.deepStrictEqual(weakMap1, weakMap2);
// AssertionError: Values have same structure but are not reference-equal:
//
// WeakMap {
//   <items unknown>
// }

// Comparing the same instance to itself succeeds
assert.deepStrictEqual(weakMap1, weakMap1);
// OK

const weakSet1 = new WeakSet();
const weakSet2 = new WeakSet();
weakSet1.add(obj);
weakSet2.add(obj);

// Comparing different instances fails, even with same contents
assert.deepStrictEqual(weakSet1, weakSet2);
// AssertionError: Values have same structure but are not reference-equal:
// + actual - expected
//
// WeakSet {
//   <items unknown>
// }

// Comparing the same instance to itself succeeds
assert.deepStrictEqual(weakSet1, weakSet1);
// OK

如果值不相等,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.doesNotMatch(string, regexp[, message])#

期望 string 输入与正则表达式不匹配。

import assert from 'node:assert/strict';

assert.doesNotMatch('I will fail', /fail/);
// AssertionError [ERR_ASSERTION]: The input was expected to not match the ...

assert.doesNotMatch(123, /pass/);
// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.

assert.doesNotMatch('I will pass', /different/);
// OKconst assert = require('node:assert/strict');

assert.doesNotMatch('I will fail', /fail/);
// AssertionError [ERR_ASSERTION]: The input was expected to not match the ...

assert.doesNotMatch(123, /pass/);
// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.

assert.doesNotMatch('I will pass', /different/);
// OK

如果值确实匹配,或者 string 参数不是 string 类型,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.doesNotReject(asyncFn[, error][, message])#

等待 asyncFn promise 完成,或者如果 asyncFn 是一个函数,则立即调用该函数并等待返回的 promise 完成。然后它将检查该 promise 没有被拒绝(rejected)。

如果 asyncFn 是一个函数并且它同步地抛出一个错误,assert.doesNotReject() 将返回一个带有该错误的被拒绝的 Promise。如果该函数不返回一个 promise,assert.doesNotReject() 将返回一个带有 ERR_INVALID_RETURN_VALUE 错误的被拒绝的 Promise。在这两种情况下,错误处理程序都会被跳过。

使用 assert.doesNotReject() 实际上没什么用,因为捕获一个拒绝然后再次拒绝它的好处很小。相反,可以考虑在不应该拒绝的特定代码路径旁边添加注释,并保持错误消息尽可能具有表现力。

如果指定了 error,它可以是一个Class<RegExp> 或一个验证函数。更多详情请参阅 assert.throws()

除了等待完成的异步特性外,其行为与 assert.doesNotThrow() 完全相同。

import assert from 'node:assert/strict';

await assert.doesNotReject(
  async () => {
    throw new TypeError('Wrong value');
  },
  SyntaxError,
);const assert = require('node:assert/strict');

(async () => {
  await assert.doesNotReject(
    async () => {
      throw new TypeError('Wrong value');
    },
    SyntaxError,
  );
})();
import assert from 'node:assert/strict';

assert.doesNotReject(Promise.reject(new TypeError('Wrong value')))
  .then(() => {
    // ...
  });const assert = require('node:assert/strict');

assert.doesNotReject(Promise.reject(new TypeError('Wrong value')))
  .then(() => {
    // ...
  });

assert.doesNotThrow(fn[, error][, message])#

断言函数 fn 不会抛出错误。

使用 assert.doesNotThrow() 实际上没什么用,因为捕获一个错误然后重新抛出它没有任何好处。相反,可以考虑在不应该抛出错误的特定代码路径旁边添加注释,并保持错误消息尽可能具有表现力。

当调用 assert.doesNotThrow() 时,它将立即调用 fn 函数。

如果抛出了一个错误,并且该错误的类型与 error 参数指定的类型相同,则会抛出一个 AssertionError。如果错误的类型不同,或者 error 参数未定义,则该错误会传播回调用者。

如果指定了 error,它可以是一个Class<RegExp> 或一个验证函数。更多详情请参阅 assert.throws()

例如,以下代码将抛出 <TypeError>,因为断言中没有匹配的错误类型。

import assert from 'node:assert/strict';

assert.doesNotThrow(
  () => {
    throw new TypeError('Wrong value');
  },
  SyntaxError,
);const assert = require('node:assert/strict');

assert.doesNotThrow(
  () => {
    throw new TypeError('Wrong value');
  },
  SyntaxError,
);

但是,以下代码将导致一个 AssertionError,其消息为“Got unwanted exception...”(收到了不期望的异常...)。

import assert from 'node:assert/strict';

assert.doesNotThrow(
  () => {
    throw new TypeError('Wrong value');
  },
  TypeError,
);const assert = require('node:assert/strict');

assert.doesNotThrow(
  () => {
    throw new TypeError('Wrong value');
  },
  TypeError,
);

如果抛出了 AssertionError 并且为 message 参数提供了值,则 message 的值将被附加到 AssertionError 消息中。

import assert from 'node:assert/strict';

assert.doesNotThrow(
  () => {
    throw new TypeError('Wrong value');
  },
  /Wrong value/,
  'Whoops',
);
// Throws: AssertionError: Got unwanted exception: Whoopsconst assert = require('node:assert/strict');

assert.doesNotThrow(
  () => {
    throw new TypeError('Wrong value');
  },
  /Wrong value/,
  'Whoops',
);
// Throws: AssertionError: Got unwanted exception: Whoops

assert.equal(actual, expected[, message])#

严格断言模式

assert.strictEqual() 的别名。

旧式断言模式

稳定性: 3 - 旧式: 请改用 assert.strictEqual()

使用== 运算符测试 actualexpected 参数之间的浅层、强制类型转换的相等性。NaN 会被特殊处理,如果两侧都是 NaN,则被视为相同。

import assert from 'node:assert';

assert.equal(1, 1);
// OK, 1 == 1
assert.equal(1, '1');
// OK, 1 == '1'
assert.equal(NaN, NaN);
// OK

assert.equal(1, 2);
// AssertionError: 1 == 2
assert.equal({ a: { b: 1 } }, { a: { b: 1 } });
// AssertionError: { a: { b: 1 } } == { a: { b: 1 } }const assert = require('node:assert');

assert.equal(1, 1);
// OK, 1 == 1
assert.equal(1, '1');
// OK, 1 == '1'
assert.equal(NaN, NaN);
// OK

assert.equal(1, 2);
// AssertionError: 1 == 2
assert.equal({ a: { b: 1 } }, { a: { b: 1 } });
// AssertionError: { a: { b: 1 } } == { a: { b: 1 } }

如果值不相等,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.fail([message])#

抛出一个带有提供的错误消息或默认错误消息的 AssertionError。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

import assert from 'node:assert/strict';

assert.fail();
// AssertionError [ERR_ASSERTION]: Failed

assert.fail('boom');
// AssertionError [ERR_ASSERTION]: boom

assert.fail(new TypeError('need array'));
// TypeError: need arrayconst assert = require('node:assert/strict');

assert.fail();
// AssertionError [ERR_ASSERTION]: Failed

assert.fail('boom');
// AssertionError [ERR_ASSERTION]: boom

assert.fail(new TypeError('need array'));
// TypeError: need array

assert.ifError(value)#

如果 value 不是 undefinednull,则抛出 value。这在测试回调函数中的 error 参数时很有用。堆栈跟踪包含从传递给 ifError() 的错误中的所有帧,包括可能为 ifError() 本身新增的帧。

import assert from 'node:assert/strict';

assert.ifError(null);
// OK
assert.ifError(0);
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 0
assert.ifError('error');
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error'
assert.ifError(new Error());
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error

// Create some random error frames.
let err;
(function errorFrame() {
  err = new Error('test error');
})();

(function ifErrorFrame() {
  assert.ifError(err);
})();
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
//     at ifErrorFrame
//     at errorFrameconst assert = require('node:assert/strict');

assert.ifError(null);
// OK
assert.ifError(0);
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 0
assert.ifError('error');
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error'
assert.ifError(new Error());
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error

// Create some random error frames.
let err;
(function errorFrame() {
  err = new Error('test error');
})();

(function ifErrorFrame() {
  assert.ifError(err);
})();
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
//     at ifErrorFrame
//     at errorFrame

assert.match(string, regexp[, message])#

期望 string 输入与正则表达式匹配。

import assert from 'node:assert/strict';

assert.match('I will fail', /pass/);
// AssertionError [ERR_ASSERTION]: The input did not match the regular ...

assert.match(123, /pass/);
// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.

assert.match('I will pass', /pass/);
// OKconst assert = require('node:assert/strict');

assert.match('I will fail', /pass/);
// AssertionError [ERR_ASSERTION]: The input did not match the regular ...

assert.match(123, /pass/);
// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.

assert.match('I will pass', /pass/);
// OK

如果值不匹配,或者 string 参数不是 string 类型,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.notDeepEqual(actual, expected[, message])#

严格断言模式

assert.notDeepStrictEqual() 的别名。

旧式断言模式

测试任何深度不相等性。与 assert.deepEqual() 相反。

import assert from 'node:assert';

const obj1 = {
  a: {
    b: 1,
  },
};
const obj2 = {
  a: {
    b: 2,
  },
};
const obj3 = {
  a: {
    b: 1,
  },
};
const obj4 = { __proto__: obj1 };

assert.notDeepEqual(obj1, obj1);
// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }

assert.notDeepEqual(obj1, obj2);
// OK

assert.notDeepEqual(obj1, obj3);
// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }

assert.notDeepEqual(obj1, obj4);
// OKconst assert = require('node:assert');

const obj1 = {
  a: {
    b: 1,
  },
};
const obj2 = {
  a: {
    b: 2,
  },
};
const obj3 = {
  a: {
    b: 1,
  },
};
const obj4 = { __proto__: obj1 };

assert.notDeepEqual(obj1, obj1);
// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }

assert.notDeepEqual(obj1, obj2);
// OK

assert.notDeepEqual(obj1, obj3);
// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }

assert.notDeepEqual(obj1, obj4);
// OK

如果值是深度相等的,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.notDeepStrictEqual(actual, expected[, message])#

测试深度严格不相等性。与 assert.deepStrictEqual() 相反。

import assert from 'node:assert/strict';

assert.notDeepStrictEqual({ a: 1 }, { a: '1' });
// OKconst assert = require('node:assert/strict');

assert.notDeepStrictEqual({ a: 1 }, { a: '1' });
// OK

如果值是深度且严格相等的,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.notEqual(actual, expected[, message])#

严格断言模式

assert.notStrictEqual() 的别名。

旧式断言模式

稳定性: 3 - 旧式: 请改用 assert.notStrictEqual()

使用!= 运算符测试浅层、强制类型转换的不相等性。NaN 会被特殊处理,如果两侧都是 NaN,则被视为相同。

import assert from 'node:assert';

assert.notEqual(1, 2);
// OK

assert.notEqual(1, 1);
// AssertionError: 1 != 1

assert.notEqual(1, '1');
// AssertionError: 1 != '1'const assert = require('node:assert');

assert.notEqual(1, 2);
// OK

assert.notEqual(1, 1);
// AssertionError: 1 != 1

assert.notEqual(1, '1');
// AssertionError: 1 != '1'

如果值相等,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.notStrictEqual(actual, expected[, message])#

测试由 Object.is() 决定的 actualexpected 参数之间的严格不相等性。

import assert from 'node:assert/strict';

assert.notStrictEqual(1, 2);
// OK

assert.notStrictEqual(1, 1);
// AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to:
//
// 1

assert.notStrictEqual(1, '1');
// OKconst assert = require('node:assert/strict');

assert.notStrictEqual(1, 2);
// OK

assert.notStrictEqual(1, 1);
// AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to:
//
// 1

assert.notStrictEqual(1, '1');
// OK

如果值是严格相等的,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.ok(value[, message])#

测试 value 是否为真值(truthy)。它等同于 assert.equal(!!value, true, message)

如果 value 不是真值,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数是 undefined,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError。如果根本没有传入任何参数,message 将被设置为字符串:'No value argument passed to `assert.ok()`'(没有值参数传递给 `assert.ok()`)。

请注意,在 repl 中,错误消息将与在文件中抛出的错误消息不同!详情见下文。

import assert from 'node:assert/strict';

assert.ok(true);
// OK
assert.ok(1);
// OK

assert.ok();
// AssertionError: No value argument passed to `assert.ok()`

assert.ok(false, 'it\'s false');
// AssertionError: it's false

// In the repl:
assert.ok(typeof 123 === 'string');
// AssertionError: false == true

// In a file (e.g. test.js):
assert.ok(typeof 123 === 'string');
// AssertionError: The expression evaluated to a falsy value:
//
//   assert.ok(typeof 123 === 'string')

assert.ok(false);
// AssertionError: The expression evaluated to a falsy value:
//
//   assert.ok(false)

assert.ok(0);
// AssertionError: The expression evaluated to a falsy value:
//
//   assert.ok(0)const assert = require('node:assert/strict');

assert.ok(true);
// OK
assert.ok(1);
// OK

assert.ok();
// AssertionError: No value argument passed to `assert.ok()`

assert.ok(false, 'it\'s false');
// AssertionError: it's false

// In the repl:
assert.ok(typeof 123 === 'string');
// AssertionError: false == true

// In a file (e.g. test.js):
assert.ok(typeof 123 === 'string');
// AssertionError: The expression evaluated to a falsy value:
//
//   assert.ok(typeof 123 === 'string')

assert.ok(false);
// AssertionError: The expression evaluated to a falsy value:
//
//   assert.ok(false)

assert.ok(0);
// AssertionError: The expression evaluated to a falsy value:
//
//   assert.ok(0)
import assert from 'node:assert/strict';

// Using `assert()` works the same:
assert(0);
// AssertionError: The expression evaluated to a falsy value:
//
//   assert(0)const assert = require('node:assert');

// Using `assert()` works the same:
assert(0);
// AssertionError: The expression evaluated to a falsy value:
//
//   assert(0)

assert.rejects(asyncFn[, error][, message])#

等待 asyncFn promise 完成,或者如果 asyncFn 是一个函数,则立即调用该函数并等待返回的 promise 完成。然后它将检查该 promise 被拒绝(rejected)。

如果 asyncFn 是一个函数并且它同步地抛出一个错误,assert.rejects() 将返回一个带有该错误的被拒绝的 Promise。如果该函数不返回一个 promise,assert.rejects() 将返回一个带有 ERR_INVALID_RETURN_VALUE 错误的被拒绝的 Promise。在这两种情况下,错误处理程序都会被跳过。

除了等待完成的异步特性外,其行为与 assert.throws() 完全相同。

如果指定了 error,它可以是一个Class<RegExp>、一个验证函数、一个每个属性都会被测试的对象,或者一个每个属性(包括不可枚举的 messagename 属性)都会被测试的错误实例。

如果指定了 message,它将作为 asyncFn 未能拒绝时 AssertionError 提供的消息。

import assert from 'node:assert/strict';

await assert.rejects(
  async () => {
    throw new TypeError('Wrong value');
  },
  {
    name: 'TypeError',
    message: 'Wrong value',
  },
);const assert = require('node:assert/strict');

(async () => {
  await assert.rejects(
    async () => {
      throw new TypeError('Wrong value');
    },
    {
      name: 'TypeError',
      message: 'Wrong value',
    },
  );
})();
import assert from 'node:assert/strict';

await assert.rejects(
  async () => {
    throw new TypeError('Wrong value');
  },
  (err) => {
    assert.strictEqual(err.name, 'TypeError');
    assert.strictEqual(err.message, 'Wrong value');
    return true;
  },
);const assert = require('node:assert/strict');

(async () => {
  await assert.rejects(
    async () => {
      throw new TypeError('Wrong value');
    },
    (err) => {
      assert.strictEqual(err.name, 'TypeError');
      assert.strictEqual(err.message, 'Wrong value');
      return true;
    },
  );
})();
import assert from 'node:assert/strict';

assert.rejects(
  Promise.reject(new Error('Wrong value')),
  Error,
).then(() => {
  // ...
});const assert = require('node:assert/strict');

assert.rejects(
  Promise.reject(new Error('Wrong value')),
  Error,
).then(() => {
  // ...
});

error 不能是字符串。如果一个字符串作为第二个参数提供,那么 error 将被假定为省略,而该字符串将用于 message。这可能导致容易忽略的错误。如果考虑使用字符串作为第二个参数,请仔细阅读 assert.throws() 中的示例。

assert.strictEqual(actual, expected[, message])#

测试由 Object.is() 决定的 actualexpected 参数之间的严格相等性。

import assert from 'node:assert/strict';

assert.strictEqual(1, 2);
// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
//
// 1 !== 2

assert.strictEqual(1, 1);
// OK

assert.strictEqual('Hello foobar', 'Hello World!');
// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
// + actual - expected
//
// + 'Hello foobar'
// - 'Hello World!'
//          ^

const apples = 1;
const oranges = 2;
assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`);
// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2

assert.strictEqual(1, '1', new TypeError('Inputs are not identical'));
// TypeError: Inputs are not identicalconst assert = require('node:assert/strict');

assert.strictEqual(1, 2);
// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
//
// 1 !== 2

assert.strictEqual(1, 1);
// OK

assert.strictEqual('Hello foobar', 'Hello World!');
// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
// + actual - expected
//
// + 'Hello foobar'
// - 'Hello World!'
//          ^

const apples = 1;
const oranges = 2;
assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`);
// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2

assert.strictEqual(1, '1', new TypeError('Inputs are not identical'));
// TypeError: Inputs are not identical

如果值不是严格相等的,则会抛出一个 AssertionError,其 message 属性等于 message 参数的值。如果 message 参数未定义,则会分配一个默认的错误消息。如果 message 参数是 <Error> 的实例,则会抛出该 <Error> 实例而不是 AssertionError

assert.throws(fn[, error][, message])#

期望函数 fn 抛出一个错误。

如果指定了 error,它可以是一个Class<RegExp>、一个验证函数、一个每个属性都会进行严格深度相等测试的验证对象,或者一个每个属性(包括不可枚举的 messagename 属性)都会进行严格深度相等测试的错误实例。当使用对象时,也可以使用正则表达式来验证字符串属性。示例见下文。

如果指定了 message,它将被附加到 fn 调用未能抛出错误或错误验证失败时由 AssertionError 提供的消息中。

自定义验证对象/错误实例

import assert from 'node:assert/strict';

const err = new TypeError('Wrong value');
err.code = 404;
err.foo = 'bar';
err.info = {
  nested: true,
  baz: 'text',
};
err.reg = /abc/i;

assert.throws(
  () => {
    throw err;
  },
  {
    name: 'TypeError',
    message: 'Wrong value',
    info: {
      nested: true,
      baz: 'text',
    },
    // Only properties on the validation object will be tested for.
    // Using nested objects requires all properties to be present. Otherwise
    // the validation is going to fail.
  },
);

// Using regular expressions to validate error properties:
assert.throws(
  () => {
    throw err;
  },
  {
    // The `name` and `message` properties are strings and using regular
    // expressions on those will match against the string. If they fail, an
    // error is thrown.
    name: /^TypeError$/,
    message: /Wrong/,
    foo: 'bar',
    info: {
      nested: true,
      // It is not possible to use regular expressions for nested properties!
      baz: 'text',
    },
    // The `reg` property contains a regular expression and only if the
    // validation object contains an identical regular expression, it is going
    // to pass.
    reg: /abc/i,
  },
);

// Fails due to the different `message` and `name` properties:
assert.throws(
  () => {
    const otherErr = new Error('Not found');
    // Copy all enumerable properties from `err` to `otherErr`.
    for (const [key, value] of Object.entries(err)) {
      otherErr[key] = value;
    }
    throw otherErr;
  },
  // The error's `message` and `name` properties will also be checked when using
  // an error as validation object.
  err,
);const assert = require('node:assert/strict');

const err = new TypeError('Wrong value');
err.code = 404;
err.foo = 'bar';
err.info = {
  nested: true,
  baz: 'text',
};
err.reg = /abc/i;

assert.throws(
  () => {
    throw err;
  },
  {
    name: 'TypeError',
    message: 'Wrong value',
    info: {
      nested: true,
      baz: 'text',
    },
    // Only properties on the validation object will be tested for.
    // Using nested objects requires all properties to be present. Otherwise
    // the validation is going to fail.
  },
);

// Using regular expressions to validate error properties:
assert.throws(
  () => {
    throw err;
  },
  {
    // The `name` and `message` properties are strings and using regular
    // expressions on those will match against the string. If they fail, an
    // error is thrown.
    name: /^TypeError$/,
    message: /Wrong/,
    foo: 'bar',
    info: {
      nested: true,
      // It is not possible to use regular expressions for nested properties!
      baz: 'text',
    },
    // The `reg` property contains a regular expression and only if the
    // validation object contains an identical regular expression, it is going
    // to pass.
    reg: /abc/i,
  },
);

// Fails due to the different `message` and `name` properties:
assert.throws(
  () => {
    const otherErr = new Error('Not found');
    // Copy all enumerable properties from `err` to `otherErr`.
    for (const [key, value] of Object.entries(err)) {
      otherErr[key] = value;
    }
    throw otherErr;
  },
  // The error's `message` and `name` properties will also be checked when using
  // an error as validation object.
  err,
);

使用构造函数验证 instanceof

import assert from 'node:assert/strict';

assert.throws(
  () => {
    throw new Error('Wrong value');
  },
  Error,
);const assert = require('node:assert/strict');

assert.throws(
  () => {
    throw new Error('Wrong value');
  },
  Error,
);

使用 <RegExp> 验证错误消息

使用正则表达式会对错误对象运行 .toString,因此也会包含错误名称。

import assert from 'node:assert/strict';

assert.throws(
  () => {
    throw new Error('Wrong value');
  },
  /^Error: Wrong value$/,
);const assert = require('node:assert/strict');

assert.throws(
  () => {
    throw new Error('Wrong value');
  },
  /^Error: Wrong value$/,
);

自定义错误验证

该函数必须返回 true 以表示所有内部验证都通过了。否则,它将因 AssertionError 而失败。

import assert from 'node:assert/strict';

assert.throws(
  () => {
    throw new Error('Wrong value');
  },
  (err) => {
    assert(err instanceof Error);
    assert(/value/.test(err));
    // Avoid returning anything from validation functions besides `true`.
    // Otherwise, it's not clear what part of the validation failed. Instead,
    // throw an error about the specific validation that failed (as done in this
    // example) and add as much helpful debugging information to that error as
    // possible.
    return true;
  },
  'unexpected error',
);const assert = require('node:assert/strict');

assert.throws(
  () => {
    throw new Error('Wrong value');
  },
  (err) => {
    assert(err instanceof Error);
    assert(/value/.test(err));
    // Avoid returning anything from validation functions besides `true`.
    // Otherwise, it's not clear what part of the validation failed. Instead,
    // throw an error about the specific validation that failed (as done in this
    // example) and add as much helpful debugging information to that error as
    // possible.
    return true;
  },
  'unexpected error',
);

error 不能是字符串。如果一个字符串作为第二个参数提供,那么 error 将被假定为省略,而该字符串将用于 message。这可能导致容易忽略的错误。使用与抛出的错误消息相同的消息将导致一个 ERR_AMBIGUOUS_ARGUMENT 错误。如果考虑使用字符串作为第二个参数,请仔细阅读下面的示例。

import assert from 'node:assert/strict';

function throwingFirst() {
  throw new Error('First');
}

function throwingSecond() {
  throw new Error('Second');
}

function notThrowing() {}

// The second argument is a string and the input function threw an Error.
// The first case will not throw as it does not match for the error message
// thrown by the input function!
assert.throws(throwingFirst, 'Second');
// In the next example the message has no benefit over the message from the
// error and since it is not clear if the user intended to actually match
// against the error message, Node.js throws an `ERR_AMBIGUOUS_ARGUMENT` error.
assert.throws(throwingSecond, 'Second');
// TypeError [ERR_AMBIGUOUS_ARGUMENT]

// The string is only used (as message) in case the function does not throw:
assert.throws(notThrowing, 'Second');
// AssertionError [ERR_ASSERTION]: Missing expected exception: Second

// If it was intended to match for the error message do this instead:
// It does not throw because the error messages match.
assert.throws(throwingSecond, /Second$/);

// If the error message does not match, an AssertionError is thrown.
assert.throws(throwingFirst, /Second$/);
// AssertionError [ERR_ASSERTION]const assert = require('node:assert/strict');

function throwingFirst() {
  throw new Error('First');
}

function throwingSecond() {
  throw new Error('Second');
}

function notThrowing() {}

// The second argument is a string and the input function threw an Error.
// The first case will not throw as it does not match for the error message
// thrown by the input function!
assert.throws(throwingFirst, 'Second');
// In the next example the message has no benefit over the message from the
// error and since it is not clear if the user intended to actually match
// against the error message, Node.js throws an `ERR_AMBIGUOUS_ARGUMENT` error.
assert.throws(throwingSecond, 'Second');
// TypeError [ERR_AMBIGUOUS_ARGUMENT]

// The string is only used (as message) in case the function does not throw:
assert.throws(notThrowing, 'Second');
// AssertionError [ERR_ASSERTION]: Missing expected exception: Second

// If it was intended to match for the error message do this instead:
// It does not throw because the error messages match.
assert.throws(throwingSecond, /Second$/);

// If the error message does not match, an AssertionError is thrown.
assert.throws(throwingFirst, /Second$/);
// AssertionError [ERR_ASSERTION]

由于这种表示法容易出错且令人困惑,请避免使用字符串作为第二个参数。

assert.partialDeepStrictEqual(actual, expected[, message])#

测试 actualexpected 参数之间的部分深度相等性。“深度”相等意味着子对象的可枚举“自有”属性也按照以下规则进行递归评估。“部分”相等意味着只比较存在于 expected 参数上的属性。

该方法总是能通过与 assert.deepStrictEqual() 相同的测试用例,其行为是后者的一个超集。

比较详情#

  • 原始值使用 Object.is() 进行比较。
  • 对象的类型标签应该相同。
  • 不比较对象的 [[Prototype]]
  • 只考虑可枚举的“自有”属性
  • 总是比较 <Error> 的名称、消息、原因(cause)和错误(errors),即使这些属性不是可枚举的。也会比较 errors
  • 也会比较可枚举的自有 <Symbol> 属性。
  • 对象包装器会同时作为对象和解包后的值进行比较。
  • Object 属性以无序方式进行比较。
  • <Map> 的键和 <Set> 的项以无序方式进行比较。
  • 当两侧不同或两侧都遇到循环引用时,递归停止。
  • <WeakMap><WeakSet><Promise> 实例进行结构化比较。它们仅在引用同一个对象时才相等。任何不同的 WeakMapWeakSetPromise 实例之间的比较都会导致不相等,即使它们包含相同的内容。
  • 总是比较 <RegExp> 的 lastIndex、flags 和 source,即使这些属性是不可枚举的。
  • 忽略稀疏数组中的空洞(holes)。
import assert from 'node:assert';

assert.partialDeepStrictEqual(
  { a: { b: { c: 1 } } },
  { a: { b: { c: 1 } } },
);
// OK

assert.partialDeepStrictEqual(
  { a: 1, b: 2, c: 3 },
  { b: 2 },
);
// OK

assert.partialDeepStrictEqual(
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [4, 5, 8],
);
// OK

assert.partialDeepStrictEqual(
  new Set([{ a: 1 }, { b: 1 }]),
  new Set([{ a: 1 }]),
);
// OK

assert.partialDeepStrictEqual(
  new Map([['key1', 'value1'], ['key2', 'value2']]),
  new Map([['key2', 'value2']]),
);
// OK

assert.partialDeepStrictEqual(123n, 123n);
// OK

assert.partialDeepStrictEqual(
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [5, 4, 8],
);
// AssertionError

assert.partialDeepStrictEqual(
  { a: 1 },
  { a: 1, b: 2 },
);
// AssertionError

assert.partialDeepStrictEqual(
  { a: { b: 2 } },
  { a: { b: '2' } },
);
// AssertionErrorconst assert = require('node:assert');

assert.partialDeepStrictEqual(
  { a: { b: { c: 1 } } },
  { a: { b: { c: 1 } } },
);
// OK

assert.partialDeepStrictEqual(
  { a: 1, b: 2, c: 3 },
  { b: 2 },
);
// OK

assert.partialDeepStrictEqual(
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [4, 5, 8],
);
// OK

assert.partialDeepStrictEqual(
  new Set([{ a: 1 }, { b: 1 }]),
  new Set([{ a: 1 }]),
);
// OK

assert.partialDeepStrictEqual(
  new Map([['key1', 'value1'], ['key2', 'value2']]),
  new Map([['key2', 'value2']]),
);
// OK

assert.partialDeepStrictEqual(123n, 123n);
// OK

assert.partialDeepStrictEqual(
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [5, 4, 8],
);
// AssertionError

assert.partialDeepStrictEqual(
  { a: 1 },
  { a: 1, b: 2 },
);
// AssertionError

assert.partialDeepStrictEqual(
  { a: { b: 2 } },
  { a: { b: '2' } },
);
// AssertionError