.match / regexp 正则匹配与 .satisfy 自定义谓词
.match —— 正则匹配
expect('hello123').to.match(/^\w+\d+$/); // 通过
expect('user@example.com').to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/); // 通过
expect('INVALID').to.match(/^\d+$/); // 失败
参数可以是 RegExp 对象也可以是正则字符串(建议用 RegExp 字面量,避免转义):
expect('hello').to.match(/^h/);
expect('hello').to.match('^h'); // 也可以,但转义麻烦
配合 .not:
expect('password123').to.not.match(/\s/); // 不应该包含空白字符
.regexp —— .match 的别名
expect('hello').to.regexp(/^h/); // 同 .match
完全等价,选哪个都行。
.satisfy —— 自定义谓词
当内置断言不够用时,.satisfy 接受一个函数,返回 boolean:
// 自定义:必须是偶数
expect(42).to.satisfy(n => n % 2 === 0); // 通过
expect(43).to.satisfy(n => n % 2 === 0); // 失败
// 自定义:字符串长度在 [5, 10] 之间且首字母大写
expect('Hello').to.satisfy(s => {
return s.length >= 5 && s.length <= 10 && /^[A-Z]/.test(s);
});
函数的第一个参数是被断言的值,返回 true 表示通过,false 表示失败。
.satisfy 的错误消息
.satisfy 失败时的错误消息信息量有限:
expect(43).to.satisfy(n => n % 2 === 0);
// AssertionError: expected 43 to satisfy [Function]
函数名如果有帮助会更好:
function isEven(n) { return n % 2 === 0; }
expect(43).to.satisfy(isEven);
// AssertionError: expected 43 to satisfy isEven
或者加自定义消息(expect 风格支持):
expect(43, '应该是偶数').to.satisfy(n => n % 2 === 0);
// AssertionError: 应该是偶数: expected 43 to satisfy [Function]
何时用 .satisfy
- 逻辑太复杂,正则写不出来或写出来难读。
- 涉及多个值的组合判断(需要闭包捕获额外变量)。
- 一次性使用的断言逻辑,不值得写成插件。
何时不用 .satisfy
- 内置断言已经能覆盖(
expect(x).to.be.above(10)比expect(x).to.satisfy(n => n > 10)清晰得多)。 - 断言在多个测试中复用 → 考虑写成 Chai 插件或自定义 helper。
assert 风格中的 .match / .satisfy
assert.match('hello', /^h/); // assert.match (v5+)
assert.notMatch('hello', /^w/);
// assert 风格没有 satisfy,用 assert(expression) 代替
assert(n % 2 === 0, '应该是偶数');
正则常见场景
邮箱格式
expect(email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
URL 格式
expect(url).to.match(/^https?:\/\/[^\s]+$/);
只包含安全字符(XSS 防护)
expect(input).to.not.match(/[<>"]/); // 不包含 < > "
密码强度
expect(password).to.match(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/);
// 至少 8 字符,含数字、小写、大写