第三章 断言类型:相等性断言、真值断言、包含断言、类型断言

断言这东西,说白了就是给代码装个「安检门」。你写了一个函数,它返回什么?你心里有数。但代码跑起来,鬼知道中间会不会出幺蛾子。断言就是帮你盯着这些关键节点的。

我个人习惯把断言分成四大类:相等性、真值、包含、类型。这四类基本覆盖了日常测试中 90% 的场景。咱们一个一个聊。

3.1 相等性断言

这是最基础、最常用的断言。你写了个加法函数,期望 1+1 等于 2,那就用相等性断言来验证。

但这里有个坑——严格相等和宽松相等的区别。我见过不少新手在这上面翻车。

核心要点:

  • 严格相等(===):值和类型都必须一致。比如 1 === 1 是 true,但 1 === '1' 是 false。
  • 宽松相等(==):只比较值,会做类型转换。比如 1 == '1' 是 true。

我在项目中遇到过一个问题:后端返回了一个数字 200,前端断言时用了宽松相等,结果某天接口改了返回字符串 '200',测试居然通过了。上线后页面直接崩了。嗯,从那以后我团队里统一要求:能用严格相等就别用宽松的。

// 常见测试框架中的相等性断言
// Jest 示例
expect(add(1, 1)).toBe(2);           // 严格相等
expect(add(1, 1)).toEqual(2);        // 深度相等(对象递归比较)

// 对象比较时要注意
expect({ a: 1 }).toEqual({ a: 1 });  // 通过
expect({ a: 1 }).toBe({ a: 1 });     // 不通过,引用不同

小技巧:比较对象或数组时,用 toEqual 而不是 toBe。toBe 比较的是引用地址,toEqual 比较的是值结构。你想想看,两个长得一模一样的对象,在内存里可能是两个不同的东西。

3.2 真值断言

真值断言,就是判断一个值「是不是真的」。但这里的「真」不是布尔值 true,而是真值(truthy)和假值(falsy)的概念。

JavaScript 里,以下值都是假值:

  • false
  • 0
  • ''(空字符串)
  • null
  • undefined
  • NaN

除此之外,其他都是真值。包括空数组 []、空对象 {},它们都是真值。

// 真值断言示例
expect(someValue).toBeTruthy();   // 判断是否为真值
expect(someValue).toBeFalsy();    // 判断是否为假值
expect(someValue).toBeNull();     // 专门判断 null
expect(someValue).toBeUndefined();// 专门判断 undefined

我曾经踩过一个坑:判断一个数组是否为空,我用的是 toBeFalsy()。结果数组是 [],它居然是 truthy 的!测试通过了,但逻辑完全错了。后来我学乖了,判断数组长度用 .toHaveLength(0)。

注意:不要用 toBeTruthy() 来判断「存在性」。比如一个变量可能是 0,0 是 falsy,但 0 本身是一个有效值。这时候应该用 toBeDefined() 或 toBeNull() 来精确判断。

3.3 包含断言

包含断言,就是检查某个东西「在不在」另一个东西里面。字符串、数组、对象都能用。

我个人觉得这是最实用的一类断言。为什么?因为很多时候我们不关心精确值,只关心「有没有包含某个关键信息」。

// 字符串包含
expect('Hello World').toContain('World');  // 通过

// 数组包含
expect([1, 2, 3]).toContain(2);            // 通过

// 对象属性包含
expect({ name: 'Tom', age: 18 }).toHaveProperty('name');
expect({ name: 'Tom', age: 18 }).toHaveProperty('age', 18);

我在项目中遇到过这样一个场景:接口返回的 HTML 片段里,我需要确认是否包含某个特定的 CSS 类名。用 toContain 直接检查字符串,比解析 DOM 树快多了。当然,如果你要精确验证 DOM 结构,那还是得用专门的工具。

使用建议:包含断言适合做「快速验证」,但不适合做「精确验证」。比如你检查返回数据里有没有 id 字段,用 toHaveProperty 就够了。但如果你要验证 id 的具体值,还是用相等性断言更靠谱。

3.4 类型断言

类型断言,就是检查变量的「类型」对不对。JavaScript 是动态类型语言,变量类型在运行时可能变来变去。类型断言就是帮你抓住这些「变心」的瞬间。

// 类型断言示例
expect(typeof value).toBe('string');
expect(Array.isArray(value)).toBe(true);
expect(value).toBeInstanceOf(Date);

说实话,类型断言在纯 JavaScript 项目里用得不多。因为类型错误通常会在运行时暴露,测试反而显得多余。但在 TypeScript 项目里,类型断言就很有用了——尤其是当你需要验证某个第三方库的返回值类型时。

我记得有一次,一个同事从后端拿到的数据,文档里写的是数组,但实际返回了对象。测试里加了类型断言后,第一时间就发现了问题。要是没这个断言,这 bug 可能到线上才暴露。

类型断言的最佳实践:

  • 验证函数返回值类型(特别是外部接口)
  • 验证复杂嵌套对象的属性类型
  • 验证异步操作返回的数据结构
  • 配合 TypeScript 使用,效果更佳

3.5 四种断言的关系

这四种断言不是孤立的,它们经常组合使用。我画了一张图,帮你理清它们的关系:

断言体系 四大类型 相等性断言 toBe / toEqual 真值断言 toBeTruthy / toBeFalsy 包含断言 toContain / toHaveProperty 类型断言 toBeInstanceOf / typeof 四种断言类型覆盖了日常测试中 90% 以上的场景

从这张图能看出来,四种断言各有侧重,但最终都服务于同一个目标:验证代码行为是否符合预期。实际写测试时,我经常混着用。比如先判断返回值类型是不是对象,再判断对象里有没有某个属性,最后判断属性值是不是预期的数字。

我的个人习惯:写断言时,从「粗」到「细」。先做类型断言(确保结构对),再做包含断言(确保关键字段在),最后做相等性断言(确保具体值对)。这样一层层下来,测试既全面又不会太啰嗦。

好了,这四种断言类型就聊到这儿。每种都有它的适用场景,没有哪个是万能的。关键是根据实际情况,选最合适的那个。你写测试的时候,多想想「我要验证什么」,而不是「我要用哪个断言」,思路就清晰了。