3、断言类型详解:相等性断言、真值断言、集合断言、异常断言、类型断言

断言这东西,说白了就是给代码上「保险」。你写了一个功能,怎么证明它跑对了?靠眼睛看?不靠谱。靠日志?太慢。断言就是那个替你拍板的人——「对,这值没问题」或者「停,出事了」。

我个人习惯把断言分成五类来记。这五类基本覆盖了日常测试中 95% 的场景。咱们一个一个过。

3.1 相等性断言

这是最常用的断言,没有之一。你想想看,测试里最多的场景是什么?不就是「我调用了一个方法,它返回的值是不是我想要的」?

相等性断言又分两种:严格相等和宽松相等。

类型 方法 说明
严格相等 assert.strictEqual(actual, expected) 使用 === 比较,值和类型都必须一致
宽松相等 assert.equal(actual, expected) 使用 == 比较,会做类型转换
深度相等 assert.deepStrictEqual(actual, expected) 递归比较对象内部的所有属性
注意:我在项目中遇到过好几次因为用了 assert.equal 导致测试通过但线上出 bug 的情况。比如 assert.equal(1, '1') 居然通过了!从那以后,我团队里统一规定:所有相等性断言必须用 strictEqual,除非有特殊理由。
// 推荐写法
assert.strictEqual(add(1, 2), 3);

// 对象比较
const result = getUser({ id: 1 });
assert.deepStrictEqual(result, {
  id: 1,
  name: '张三',
  age: 25
});

3.2 真值断言

真值断言不是判断「是不是 true」,而是判断「是不是 truthy」。这两者有区别。

在 JavaScript 里,true、1、'hello'、[]、{} 都是 truthy。而 false、0、''、null、undefined、NaN 是 falsy。

常用的真值断言方法:

  • assert.isTrue(value) — 严格判断是否为 true
  • assert.isFalse(value) — 严格判断是否为 false
  • assert.ok(value) — 判断是否为 truthy
  • assert.notOk(value) — 判断是否为 falsy
我的习惯:判断「有没有值」用 assert.ok,判断「是不是 true」用 assert.isTrue。别混用,否则测试读起来会让人困惑。
// 检查用户是否存在
const user = findUser('001');
assert.ok(user, '用户应该存在');

// 检查权限
const hasPermission = checkPermission(user, 'admin');
assert.isTrue(hasPermission, '用户应该有管理员权限');

3.3 集合断言

集合断言用来判断数组、Set、Map 这类数据结构。我经常用它来验证接口返回的数据列表。

核心方法:

  • assert.include(haystack, needle) — 判断是否包含某个元素
  • assert.notInclude(haystack, needle) — 判断是否不包含
  • assert.lengthOf(array, length) — 判断数组长度
  • assert.sameMembers(set1, set2) — 判断两个数组元素相同(顺序无关)

避坑指南:我曾经在测试分页接口时,直接用 assert.equal(result.data.length, 10)。后来发现接口返回了 10 条数据,但其中有一条是重复的。从那以后,我改用 assert.sameMembers 配合去重逻辑来验证。

// 验证返回的菜单列表
const menus = await getMenus();
assert.include(menus, '首页', '菜单应该包含首页');
assert.lengthOf(menus, 5, '菜单应该有5个');

// 验证权限列表
const permissions = ['read', 'write', 'delete'];
assert.sameMembers(permissions, ['delete', 'read', 'write']);

3.4 异常断言

异常断言是用来验证「代码是否按预期抛出了错误」。很多人会忽略这个,觉得「不出错就行」。但你想过没有——如果代码该报错的时候没报错,那才是大问题。

比如用户输入了非法参数,系统应该返回 400 错误。如果它默默接受了,数据就脏了。

// 验证非法参数是否抛出异常
assert.throws(
  () => createUser({ name: '' }),
  Error,
  '用户名为空时应抛出错误'
);

// 验证异步异常
await assert.rejects(
  () => fetchData('/invalid-url'),
  /404/,
  '请求无效地址应返回404'
);
注意:异常断言一定要指定错误类型或错误信息。别只写 assert.throws(fn),那样太模糊了。万一代码因为别的原因抛错了,你的测试也会通过,这就失去了测试的意义。

3.5 类型断言

类型断言在 TypeScript 项目里特别有用。它帮你验证「返回值是不是你期望的类型」。

常用的类型断言:

  • assert.isNumber(value)
  • assert.isString(value)
  • assert.isArray(value)
  • assert.isObject(value)
  • assert.isFunction(value)
  • assert.instanceOf(value, constructor)
// 验证 API 返回的数据类型
const data = await getDashboardData();
assert.isObject(data, '返回数据应为对象');
assert.isArray(data.charts, '图表数据应为数组');
assert.isNumber(data.totalUsers, '用户总数应为数字');

// 验证自定义类
const user = new User('张三');
assert.instanceOf(user, User, 'user 应该是 User 的实例');

我的建议:类型断言最好放在测试用例的开头。先确认「东西是对的」,再验证「值是对的」。这样如果测试失败了,你能立刻知道是类型错了还是值错了,不用从头排查。

知识体系总览

下面这张图帮你理清这五类断言的关系。我画的时候特意把「相等性断言」放在中间,因为它最常用,其他四类都是它的补充。

断言类型 相等性断言 真值断言 集合断言 异常断言 类型断言 五类断言覆盖日常测试 95% 的场景 核心原则:先类型,再相等,后业务

嗯,这五类断言讲完了。你可能会问:「这么多,我该优先用哪个?」我的答案是:先类型断言确认结构,再相等性断言确认值,最后用真值或集合断言做业务验证。异常断言单独拎出来,专门测边界情况。

记住这个顺序,你的测试会清晰很多。下一节咱们聊聊断言失败时的处理策略,那个也很有意思。

可可精品资料库