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)— 严格判断是否为trueassert.isFalse(value)— 严格判断是否为falseassert.ok(value)— 判断是否为 truthyassert.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 的实例');
我的建议:类型断言最好放在测试用例的开头。先确认「东西是对的」,再验证「值是对的」。这样如果测试失败了,你能立刻知道是类型错了还是值错了,不用从头排查。
知识体系总览
下面这张图帮你理清这五类断言的关系。我画的时候特意把「相等性断言」放在中间,因为它最常用,其他四类都是它的补充。
嗯,这五类断言讲完了。你可能会问:「这么多,我该优先用哪个?」我的答案是:先类型断言确认结构,再相等性断言确认值,最后用真值或集合断言做业务验证。异常断言单独拎出来,专门测边界情况。
记住这个顺序,你的测试会清晰很多。下一节咱们聊聊断言失败时的处理策略,那个也很有意思。