.any / .all 集合逻辑修饰器 - members 和 keys 的子集/并集语义
基本概念
.any 和 .all 是集合逻辑修饰器,主要配合 .members 和 .keys 两个断言方法使用。它们分别表达:
.any:实际值包含期望值中的任意一个(OR 逻辑).all:实际值包含期望值中的所有(AND 逻辑,默认行为)
.any / .all + .keys
keys 断言检查对象的键集合:
const obj = { name: 'Alice', age: 30, email: 'a@b.com' };
// 默认:all —— 所有给定的键都要存在
expect(obj).to.have.all.keys('name', 'age'); // 通过
expect(obj).to.have.all.keys('name', 'age', 'phone'); // 失败
// any —— 给定的键中存在任意一个就行
expect(obj).to.have.any.keys('name', 'phone'); // 通过(name 存在)
expect(obj).to.have.any.keys('phone', 'fax'); // 失败(都不存在)
.any.keys 特别适合验证"对象有这些键之一"的场景:
// 用户对象应该包含身份验证相关的字段之一
expect(user).to.have.any.keys('email', 'phone', 'googleId', 'facebookId');
配合 .deep 还能处理 Set / Map:
const set = new Set(['apple', 'banana']);
expect(set).to.have.all.keys(['apple', 'banana']);
expect(set).to.have.any.keys(['apple', 'cherry']);
.any / .all + .members
members 断言是数组成员匹配:
const arr = [1, 2, 3];
// 默认:all —— 给定的所有成员都要在数组中存在
expect(arr).to.have.all.members([1, 2]); // 通过
expect(arr).to.have.all.members([1, 2, 4]); // 失败(4 不存在)
// any —— 给定的成员中存在任意一个就行
expect(arr).to.have.any.members([1, 4]); // 通过(1 存在)
expect(arr).to.have.any.members([4, 5]); // 失败(都不存在)
与 .includeMembers 的关系
.includeMembers 做的事和 .all.members 类似但语义不同:
// includeMembers:haystack 包含 needle 全部成员(子集关系)
expect([1, 2, 3]).to.includeMembers([1, 2]); // 通过
expect([1, 2, 3]).to.have.all.members([1, 2]); // 也通过
// 区别:haystack 多出来的元素
expect([1, 2, 3]).to.includeMembers([1, 2]); // 通过(haystack 可以多)
expect([1, 2, 3]).to.have.all.members([1, 2, 3]); // 通过
expect([1, 2, 3]).to.have.all.members([1, 2]); // 失败!all.members 要求 haystack 和 needle 成员完全相同
简单说:
.all.members= 成员完全匹配(集合相等).includeMembers= haystack 包含 needle(子集关系).any.members= haystack 包含 needle 中至少一个成员
修饰器组合
// .deep + .any:深度比较 + 任意键
expect([{ a: 1 }, { b: 2 }]).to.have.any.members([{ a: 1 }, { c: 3 }]); // 通过
expect([{ a: 1 }, { b: 2 }]).to.have.any.members([{ c: 3 }, { d: 4 }]); // 失败
完整语义表
| 写法 | 语义 | 示例 |
|---|---|---|
.all.members([...]) | 数组必须包含且仅包含给定成员 | [1,2,3] vs [1,2,3] ✅ / vs [1,2] ❌ |
.any.members([...]) | 数组必须包含给定成员中的至少一个 | [1,2,3] vs [1,4] ✅ / vs [4,5] ❌ |
.includeMembers([...]) | 数组必须包含给定成员(子集) | [1,2,3] vs [1,2] ✅ / vs [4] ❌ |
.all.keys('a','b') | 对象必须拥有所有给定键 | {a:1,b:2} ✅ / {a:1} ❌ |
.any.keys('a','b') | 对象必须拥有给定键中至少一个 | {a:1} ✅ / {c:1} ❌ |
实战场景
场景一:API 响应字段检查
it('用户 API 响应应该有至少一种联系方式', function () {
const res = await getUserProfile(userId);
// 应该有 email / phone / 任意社交账号之一
expect(res.body).to.have.any.keys('email', 'phone', 'twitter', 'github');
});
场景二:数组包含期望的某些值
it('推荐列表应该包含至少一个热门标签', function () {
const tags = await getRecommendTags();
expect(tags).to.have.any.members(['热门', '推荐', '编辑精选', '必看']);
});
场景三:枚举值范围验证
it('订单状态应该是已定义的几种之一', function () {
const allowedStatuses = ['pending', 'paid', 'shipped', 'delivered', 'cancelled'];
expect(order.status).to.be.oneOf(allowedStatuses); // oneOf 也是类似语义
});