Chai 语言链完整列表 - to be been is that which and has have with 等修饰词
什么是语言链
语言链(Language Chains)是 Chai 里一组可链式访问的 getter 属性。它们不执行任何断言,纯粹提升表达式的可读性。
// 以下两条断言功能完全相同
expect('hello').to.be.a('string');
expect('hello').a('string'); // 省略所有语言链
语言链让断言读起来像一句自然语言。虽然删掉也能跑,但强烈建议保留——代码首先是写给人读的。
完整列表(17 个)
| 语言链 | 示例 | 备注 |
|---|---|---|
to | expect(x).to.equal(y) | 最常用的连接词 |
be | expect(x).to.be.a('number') | 配合 a/an/exist/null 等 |
been | expect(x).to.have.been.called | 配合 sinon spy 使用 |
is | expect(x).is.a('string') | 与 be 同义 |
that | expect(arr).to.be.an('array').that.includes(1) | 引导子断言 |
which | 同 that | 引导子断言 |
and | expect(x).to.be.a('string').and.include('hi') | 连接两个断言 |
has | expect(x).has.property('name') | 配合 property / lengthOf |
have | 同 has | 配合 property / lengthOf |
with | expect(x).to.have.property('name').with.lengthOf(3) | 嵌套属性的子链 |
at | expect(x).to.be.above(100) | 配合数值比较 |
of | expect(arr).to.have.lengthOf(3) | lengthOf / members 的一部分 |
same | expect(a).to.equal(b) | 较少单独使用 |
but | 同 and | 连接两个有对比关系的断言 |
does | expect(obj).does.not.have.property('name') | 配合 not 使用 |
still | expect(x).to.still.equal(y) | 语义无区别 |
also | 同 and | 连接并列断言 |
分组理解
可以按语义把语言链分成四类:
连接类(最常用)
expect(foo).to.be.a('string');
// ↑ ↑ ↑
// to be a —— 全部是连接类
to:最通用的连接词,几乎每句都有。be/been/is:表达"是"的语义。and/also/but:连接两个连续断言。that/which:在链式中间引导新的断言。
所属类
expect(config).to.have.property('name');
// ↑ ↑
// have property 是实际断言,have 是所属类
has/have:表达"拥有"的语义。with:表达"附带"的语义,常用于嵌套链。of:表达"属于"的语义(如lengthOf)。
位置类
at:表达"在某个位置/范围"。same:表达"相同"(虽然实际少用)。
其他
been:专门给 sinon spy 使用(如expect(spy).to.have.been.called)。does:常在.not之前出现(expect(obj).does.not.have.property(...))。still:语义完全无区别,纯装饰。
语言链可以任意组合
只要组合出来的表达式读起来通顺就行,没有语法限制:
expect('hello').to.be.a('string').that.includes('ell').and.has.lengthOf(5);
expect([1, 2, 3]).has.lengthOf(3).which.includes(2);
expect({ a: 1 }).to.have.property('a').which.is.a('number').and.equal(1);
语言链 + 修饰器的组合
语言链经常和真正起作用的修饰器(.not、.deep、.any、.all)混在一起:
expect({ a: { b: 1 } }).to.have.deep.property('a.b');
// ↑ ↑ ↑ ↑
// 语言链 修饰器 语言链 实际断言(property)
修饰器的位置会影响行为——修饰器之后的断言受它影响,之前的不受。详见 .not 否定 和 .deep 修饰器。
语言链不是方法
重要:语言链是属性访问器,不是方法,所以不要加括号:
// ❌ 错误用法
expect(foo).to().be().a('string');
// ✅ 正确
expect(foo).to.be.a('string');
这个区别很关键——如果加了括号,就等于在调用 undefined,会 TypeError。
如果觉得语言链太多
实际项目中常用的语言链只有 to、be、a/an、have、and、that 这几个。其他的偶尔用到也行,用不到不用硬塞。
团队可以约定只保留最自然的表达,避免出现 expect(foo).to.of.be.also.equal(bar) 这种刻意堆砌链的写法。
下一步
- 修饰器
.not的完整用法 → .not 否定 - 修饰器
.deep深度比较 → .deep 修饰器 - 修饰器
.any/.all集合逻辑 → .any 和 .all