equal / not.equal / strictEqual 值相等比较 - 严格 vs 非严格 vs 深度
三种相等语义速查
| 写法 | JS 对应 | 适用场景 |
|---|---|---|
.equal | == | 允许类型转换,不太常用 |
.strictEqual(默认) | === | 推荐,行为可预测 |
.deep.equal | 深度相等 | 对象 / 数组的"值"比较 |
.equal —— 非严格相等
对应 == 运算符,允许类型转换:
expect(3).to.equal('3'); // 通过(3 == '3' 是 true)
expect(null).to.equal(undefined); // 通过(null == undefined 是 true)
expect([]).to.equal(''); // 通过([] == '' 是 true)
不推荐日常使用——类型转换的规则复杂且反直觉,容易掩盖 bug。Chai 默认把 .equal 绑定到严格相等是有道理的。
.strictEqual —— 严格相等(默认)
对应 ===,不做任何类型转换:
expect(3).to.strictEqual(3); // 通过
expect(3).to.strictEqual('3'); // 失败
expect(null).to.strictEqual(null); // 通过
expect(null).to.strictEqual(undefined); // 失败
这是 Chai 的默认行为——平时写的 .equal 实际上就是 .strictEqual:
// 这两条等价
expect(3).to.equal(3);
expect(3).to.strictEqual(3);
为什么 Chai 把 .equal 的行为改成了严格相等?因为 Node.js 内置 assert 的 assert.equal 也是 ==,而 assert.strictEqual 是 ===,但日常测试中大家几乎都想做严格比较。Chai 在 v4+ 把默认行为改成 ===,让 .equal 和直觉一致。
.deep.equal —— 深度相等
解决对象 / 数组的"引用 vs 值"问题:
expect({ a: 1 }).to.equal({ a: 1 }); // 失败(不同引用)
expect({ a: 1 }).to.deep.equal({ a: 1 }); // 通过(值相等)
expect([1, 2, 3]).to.equal([1, 2, 3]); // 失败
expect([1, 2, 3]).to.deep.equal([1, 2, 3]); // 通过
深度相等的完整规则见 .deep 修饰器。
.not.equal / .not.deep.equal
expect(42).to.not.equal(43);
expect({ a: 1 }).to.not.deep.equal({ a: 2 });
.notStrictEqual / .notDeepEqual
assert 风格中对应的方法名:
assert.notStrictEqual(3, '3'); // 通过(3 !== '3')
assert.notDeepEqual({ a: 1 }, { a: 2 }); // 通过
浮点精度问题
JavaScript 中 0.1 + 0.2 !== 0.3 是经典问题。直接用 .equal 会失败:
expect(0.1 + 0.2).to.equal(0.3);
// AssertionError: expected 0.30000000000000004 to equal 0.3
解决方案:
- 用
.closeTo(actual, expected, delta)—— 近似匹配 - 先
Number(x.toFixed(n))再比较 - 用专门的浮点库(decimal.js、bignumber.js)
详见下一页 .closeTo。
assert 风格完整映射
| expect / should 写法 | assert 写法 | 语义 |
|---|---|---|
.equal(a, b) | assert.strictEqual(a, b) | === |
.not.equal(a, b) | assert.notStrictEqual(a, b) | !== |
.deep.equal(a, b) | assert.deepEqual(a, b) | 深度 |
.not.deep.equal(a, b) | assert.notDeepEqual(a, b) | 深度不等 |
.eql(a, b) | assert.deepEqual(a, b) | .eql 是 .deep.equal 的别名 |
.eql 是 .deep.equal 的简写:
expect({ a: 1 }).to.eql({ a: 1 });
// 等价于
expect({ a: 1 }).to.deep.equal({ a: 1 });
错误消息对比
严格相等失败:
expect('hello').to.equal('world');
// AssertionError: expected 'hello' to equal 'world'
深度相等失败(对象差异高亮):
expect({ a: 1, b: 2 }).to.deep.equal({ a: 1, b: 3 });
// AssertionError: expected { a: 1, b: 2 } to deeply equal { a: 1, b: 3 }
// + expected - actual
// {
// a: 1,
// - b: 2
// + b: 3
// }
实践建议
- 绝大多数场景用
.equal(默认 strictEqual)。 - 涉及对象/数组/Map/Set 时加
.deep。 - 避免
.not.equal,改断言"是什么"。 - 不推荐
.equal(非严格)——类型转换太反直觉。