3. 断言类型
断言这东西,说白了就是你在代码里埋下的「检查点」。我刚开始做测试开发那会儿,总觉得断言就是简单的「等于不等于」。后来踩的坑多了,才发现断言的世界远比想象中丰富。
今天咱们就聊聊五种最常用的断言类型。每种都有它的脾气秉性,用对了事半功倍,用错了...嗯,你会后悔的。
1. 相等性断言
这是最基础、最常用的断言。你想想看,测试里最多的场景是什么?不就是「实际结果等于预期结果」吗?
相等性断言又分两种:
- 严格相等(===):值和类型都必须一致
- 宽松相等(==):只比较值,类型可以不同
我个人习惯:在测试里永远用严格相等。为什么?因为宽松相等会偷偷做类型转换,容易掩盖真正的bug。
// 严格相等
assert.strictEqual(actual, expected, '实际值应该等于预期值');
// 宽松相等(不推荐)
assert.equal(actual, expected);
我在项目中遇到过这样一个坑:一个接口返回了字符串类型的"200",而我用宽松相等去比较数字200。测试通过了,但上线后前端页面一直报错。后来才发现,接口改了返回类型,但宽松相等帮我「掩盖」了这个问题。
避坑指南:我曾经因为用宽松相等,让一个类型转换的bug在线上跑了两个月。从那以后,我的测试代码里再也看不到 == 了。
2. 真值断言
真值断言,就是判断一个值是「真」还是「假」。但这里有个容易混淆的地方——「真值」和「布尔值true」不是一回事。
在JavaScript里,以下值会被当作「假值」:
- false
- 0
- ''(空字符串)
- null
- undefined
- NaN
其他所有值都是「真值」。
// 真值断言
assert.isTrue(condition, '条件应该为真');
assert.isFalse(condition, '条件应该为假');
// 更灵活的用法
assert.ok(value, '值应该为真值');
assert.notOk(value, '值应该为假值');
你可能会问:「直接用if判断不就行了?」其实不一样。断言会提供更清晰的错误信息,而且能集成到测试框架里统一管理。
小技巧:我习惯用 assert.ok 而不是 assert.isTrue。因为 ok 更灵活,能处理各种真值场景。但如果你需要严格检查布尔值,那就用 isTrue。
3. 集合断言
集合断言,说白了就是判断「一堆东西」里有没有「某个东西」。这在测试数组、对象、Set、Map时特别有用。
常见的集合断言包括:
- 包含:数组是否包含某个元素
- 长度:集合的大小是否符合预期
- 子集:一个集合是否是另一个的子集
- 相等:两个集合的元素是否完全一致
// 数组包含
assert.include(array, element, '数组应该包含该元素');
// 数组长度
assert.lengthOf(array, expectedLength, '数组长度应该为...');
// 对象包含属性
assert.hasAllKeys(obj, ['key1', 'key2'], '对象应该包含这些属性');
// 集合相等(忽略顺序)
assert.sameMembers(array1, array2, '两个数组的元素应该相同');
我记得有一次测试一个排序功能,用 assert.equal 比较两个数组,结果一直失败。后来才发现,assert.equal 比较的是引用,而不是内容。改用 assert.sameMembers 后,问题就解决了。
关键点:集合断言最怕的就是「顺序敏感」。如果你的测试不关心顺序,一定要用忽略顺序的断言方法。
4. 异常断言
异常断言,就是测试「代码会不会抛出错误」。这听起来有点反直觉——我们不是应该避免错误吗?
其实不然。异常断言测试的是:
- 边界条件是否被正确处理
- 非法输入是否被拦截
- 错误处理逻辑是否生效
// 期望抛出异常
assert.throws(
() => { riskyFunction(); },
Error,
'应该抛出Error类型的异常'
);
// 期望不抛出异常
assert.doesNotThrow(
() => { safeFunction(); },
'不应该抛出任何异常'
);
我曾经写过一个支付模块的测试,用异常断言验证「余额不足时是否抛出InsufficientBalanceError」。结果发现,代码里根本没有这个异常类,而是直接返回了null。嗯,这就是异常断言的价值——它逼着你把错误处理做规范。
注意:异常断言一定要指定异常类型。我曾经见过有人只写 assert.throws(fn),结果代码抛了个TypeError,测试也通过了。这跟没测一样。
5. 类型断言
类型断言,就是检查变量的「类型」是否符合预期。在JavaScript这种动态类型语言里,类型断言尤其重要。
常见的类型断言包括:
- 判断是否为字符串、数字、布尔值
- 判断是否为数组、对象、函数
- 判断是否为某个类的实例
- 判断是否为 null 或 undefined
// 基本类型
assert.isString(value, '值应该是字符串');
assert.isNumber(value, '值应该是数字');
assert.isBoolean(value, '值应该是布尔值');
// 引用类型
assert.isArray(value, '值应该是数组');
assert.isObject(value, '值应该是对象');
assert.isFunction(value, '值应该是函数');
// 特殊类型
assert.isNull(value, '值应该是null');
assert.isUndefined(value, '值应该是undefined');
assert.isNaN(value, '值应该是NaN');
// 实例检查
assert.instanceOf(value, ClassName, '值应该是ClassName的实例');
你想想看,如果一个函数应该返回数组,结果返回了对象,后续所有操作都会出问题。类型断言就是最早发现这类问题的哨兵。
我的习惯:在测试每个函数的返回值时,我都会先加一个类型断言。这花不了多少时间,但能省下大量调试时间。
五种断言类型的关系
这五种断言不是孤立的,它们经常组合使用。比如:
- 先做类型断言,确认返回值是数组
- 再做集合断言,确认数组包含特定元素
- 最后做相等性断言,确认元素的值正确
这种「层层递进」的断言方式,能让你的测试更健壮,错误定位也更精准。
核心原则:断言不是越多越好,而是要「精准」。每个断言都应该有明确的测试目的,而不是为了凑数。
知识体系图
下面这张图展示了五种断言类型的关系和适用场景:
这张图把五种断言类型的关系梳理得很清楚。你可以看到,每种断言都有它独特的应用场景,组合使用才能发挥最大价值。
好了,关于断言类型就聊这么多。记住,断言是测试的「灵魂」,选对断言类型,你的测试就成功了一半。
可可精品资料库