Chai 中文文档 下载 App

assert 断言风格详解 - TDD 经典函数式调用

基本形式

const assert = require('chai').assert;

assert.equal(actual, expected);
assert.typeOf(value, 'string');
assert.lengthOf(array, 3);

assert 风格是函数式调用——每个断言是一个独立函数,不支持链式。这与 Node.js 内置 assert 模块非常相似,但功能更丰富且跨平台。

引入方式

// CommonJS
const assert = require('chai').assert;

// ESM
import { assert } from 'chai';

三种风格对比:同一个断言的写法

// 被断言的数据
const foo = 'bar';
const beverages = { tea: ['chai', 'matcha', 'oolong'] };

// expect 风格(BDD 链式)
const { expect } = require('chai');
expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(beverages).to.have.property('tea').with.lengthOf(3);

// should 风格(BDD 原型注入)
require('chai').should();
foo.should.be.a('string');
foo.should.equal('bar');
beverages.should.have.property('tea').with.lengthOf(3);

// assert 风格(TDD 函数式)
const assert = require('chai').assert;
assert.typeOf(foo, 'string');
assert.equal(foo, 'bar');
assert.property(beverages, 'tea');
assert.lengthOf(beverages.tea, 3);

assert 风格的调用方式是 assert.方法(actual, expected, [message])——把「实际值」作为第一个参数,「期望值」作为第二个参数。

消息参数

assert 风格支持为每个断言传入可选的消息参数(通常是最后一个参数),失败时拼接到错误信息中:

assert.typeOf(foo, 'string', 'foo 必须是字符串');
assert.equal(foo, 'bar', 'foo 的值应该是 bar');

常用 assert 方法速查

类型检查

assert.typeOf(value, 'string', [message]);          // 类型(小写字符串)
assert.notTypeOf(value, 'number', [message]);        // 不是某类型
assert.instanceOf(object, Class, [message]);          // 原型链
assert.notInstanceOf(object, Class, [message]);       // 不是原型链

值比较

assert.equal(actual, expected, [message]);            // 非严格等 ==
assert.notEqual(actual, expected, [message]);          // 非严格 !=
assert.strictEqual(actual, expected, [message]);      // 严格 ===
assert.notStrictEqual(actual, expected, [message]);    // 严格 !==
assert.deepEqual(actual, expected, [message]);        // 深度相等
assert.notDeepEqual(actual, expected, [message]);      // 深度不等

存在性

assert.isOk(value, [message]);        // truthy(注意方法名是 isOk 不是 isTruthy)
assert.isNotOk(value, [message]);      // falsy
assert.isNull(value, [message]);
assert.isNotNull(value, [message]);
assert.isUndefined(value, [message]);
assert.isDefined(value, [message]);
assert.exists(value, [message]);      // 非 null 非 undefined
assert.notExists(value, [message]);

布尔

assert.isTrue(value, [message]);
assert.isFalse(value, [message]);

数字比较

assert.isAtLeast(actual, expected, [message]);   // >=
assert.isAtMost(actual, expected, [message]);    // <=
assert.isAbove(actual, expected, [message]);    // >
assert.isBelow(actual, expected, [message]);     // <
assert.isWithin(value, min, max, [message]);     // 区间 [min, max]
assert.isCloseTo(actual, expected, delta, [message]);  // 近似

字符串

assert.isString(value, [message]);
assert.include(str, substring, [message]);
assert.notInclude(str, substring, [message]);
assert.lengthOf(str, n, [message]);
assert.isAbove(str.length, 0, [message]);  // 也可以用 lengthOf

数组

assert.isArray(value, [message]);
assert.lengthOf(array, n, [message]);
assert.include(array, element, [message]);
assert.notInclude(array, element, [message]);
assert.sameMembers(set1, set2, [message]);        // 无序成员相同
assert.sameDeepMembers(set1, set2, [message]);    // 深度 + 无序
assert.includeMembers(needle, haystack, [message]);  // needle 是 haystack 的子集

对象

assert.isObject(value, [message]);
assert.isPlainObject(value, [message]);
assert.property(obj, key, [message]);
assert.propertyVal(obj, key, expected, [message]);
assert.notProperty(obj, key, [message]);
assert.deepPropertyVal(obj, path, expected, [message]);
assert.hasAllKeys(obj, keys, [message]);
assert.hasAnyKeys(obj, keys, [message]);

异常

assert.throw(fn, [type], [regex], [message]);
assert.doesNotThrow(fn, [type], [regex], [message]);

自己写的表达式

assert(expression, [message]);   // 最基础的 truthy 断言
assert.fail([message]);          // 强制失败
assert.fail(actual, expected, [message], [operator]);

深度相等与嵌套属性

assert 风格中深度相等是显式方法,不需要修饰链:

// expect / should 风格
expect({ a: 1 }).to.deep.equal({ a: 1 });

// assert 风格
assert.deepEqual({ a: 1 }, { a: 1 });

嵌套属性的路径访问:

assert.deepNestedPropertyVal(
  { tea: { green: { matcha: 'yum' } } },
  'tea.green',
  { matcha: 'yum' }
);

与 Node.js 内置 assert 的关系

Chai 的 assert 风格是 Node.js 内置 assert 模块的超集——内置 assert 的方法 Chai 全有,而且 Chai 还额外提供了 .deep 系列、.members 系列、.property 系列等更多方法。

迁移代码时可以直接 const assert = require('chai').assert,不需要逐个改方法名。

什么时候选 assert 风格

assert 风格适合:

assert 风格不太适合:

下一步