.not 否定修饰器 - 链式中插入 .not 反转断言结果
基本用法
在链式中任何位置插入 .not,会否定它之后的所有断言:
expect(4).to.not.equal(5); // 通过:4 不等于 5
expect({}).to.not.have.property('name'); // 通过:空对象没有 name
expect([1, 2]).to.not.include(3); // 通过:数组不包含 3
.not 影响的是链式中它之后的部分,它之前的不受影响:
expect([1, 2, 3]).to.be.an('array').that.does.not.include(4);
// ↑ 不受 not 影响 ↑ 受 not 影响
嵌套 .not
理论上可以嵌套多个 .not,每多一个相当于翻转一次:
expect(2).to.not.not.equal(2); // 等同于 expect(2).to.equal(2)
但这种写法极度不推荐——读起来像绕口令,毫无意义。.not.not 在代码评审中应该被直接打回。
推荐 vs 不推荐用法
官方文档反复强调一个原则:推荐断言"是什么",而不是断言"不是什么"。
// ✅ 推荐:断言你期望的结果
expect(response.status).to.equal(200);
expect(result.type).to.be.a('User');
expect(form.isValid).to.be.true;
// ❌ 不推荐:断言"不是某个值"——这并不能保证是你想要的结果
expect(response.status).to.not.equal(500); // 也可能是 404
expect(result.type).to.not.be.Null; // 也可能是 string
原因:
.not匹配的空间更大——"不等于 500"可以是 200、404、302、任意值,只有一种是正确的。- 失败时
.not给出的错误信息不够有信息量。 - 过度使用
.not掩盖了测试意图的模糊性。
什么时候可以用 .not
有些场景「否定断言」是表达意图的最自然方式:
// 接口不应该报错
expect(error).to.not.exist;
// 集合不应该包含某个元素
expect(forbiddenItems).to.not.include('admin');
// 字符串不应该包含敏感词
expect(input).to.not.match(/password/);
// 对象不应该拥有某个属性(防止意外泄露)
expect(user).to.not.have.property('password');
这些例子里,断言"不应该有"比断言"应该没有 X 而有 Y"更清晰。
.not 与 .deep 的组合
修饰器可以自由组合:
// 深度不相等
expect({ a: 1 }).to.not.deep.equal({ a: 2 });
// 深度不包含某个嵌套对象
expect([{ a: 1 }]).to.not.deep.include({ a: 2 });
修饰器的顺序不影响结果:
expect({ a: 1 }).to.not.deep.equal({ a: 2 });
expect({ a: 1 }).to.deep.not.equal({ a: 2 });
// 两者等价
.not 与 .includeMembers 的搭配
includeMembers 断言 haystack 包含 needle 的所有成员(即 needle 是 haystack 的子集)。加 .not 后变成"haystack 不包含 needle 的所有成员":
expect([1, 2, 3]).to.not.includeMembers([4, 5]); // 通过
expect([1, 2, 3]).to.not.includeMembers([1, 2]); // 失败——haystack 包含 needle
这种场景下 .not 确实比反向写更清晰。
错误消息示例
正常断言失败:
expect(2).to.equal(3);
// AssertionError: expected 2 to equal 3
否定断言失败:
expect(2).to.not.equal(2);
// AssertionError: expected 2 to not equal 2
注意错误消息里会把 not 也拼进去,方便定位是哪个断言出错。
.does.not 这个组合
你可能看到 .does.not.have.property(...) 这种写法:
expect(obj).does.not.have.property('name');
这里的 .does 只是一个语言链(不做任何断言),.not 才是真正起作用的修饰器。去掉 .does 效果完全一样:
expect(obj).not.have.property('name');
expect(obj).to.not.have.property('name'); // 加 to 更自然
.does 的存在只是为了让 .not 在句子里读起来更顺。
实践建议
- 默认写正向断言。
- 遇到"不应该 X"的语义时才用
.not。 - 永远不要嵌套
.not.not。 - 结合其他修饰器(
.deep)时,用最自然的顺序就行,不用刻意排列。
下一步
- 深度相等
.deep→ .deep 修饰器 .any/.all集合逻辑 → .any 和 .all- 完整修饰器列表 → 语言链总览