Jest 断言匹配器 #
什么是匹配器? #
匹配器(Matchers)是 Jest 中用于验证测试结果的方法。Jest 提供了丰富的匹配器,可以满足各种测试场景。
javascript
test('example', () => {
expect(实际值).匹配器(预期值);
});
基本匹配器 #
toBe - 严格相等 #
使用 Object.is 进行比较,检查严格相等:
javascript
test('toBe examples', () => {
// 基本类型
expect(1 + 1).toBe(2);
expect('hello').toBe('hello');
expect(true).toBe(true);
// 对象引用(必须是同一个引用)
const obj = { a: 1 };
expect(obj).toBe(obj);
expect({ a: 1 }).not.toBe({ a: 1 }); // 不同引用
});
toEqual - 深度相等 #
递归比较对象的所有属性:
javascript
test('toEqual examples', () => {
// 对象比较
expect({ a: 1 }).toEqual({ a: 1 });
expect({ a: { b: 2 } }).toEqual({ a: { b: 2 } });
// 数组比较
expect([1, 2, 3]).toEqual([1, 2, 3]);
expect([{ id: 1 }]).toEqual([{ id: 1 }]);
// 嵌套对象
const user = {
name: 'John',
address: {
city: 'New York',
country: 'USA'
}
};
expect(user).toEqual({
name: 'John',
address: {
city: 'New York',
country: 'USA'
}
});
});
toStrictEqual - 严格深度相等 #
比 toEqual 更严格,检查类型和 undefined:
javascript
test('toStrictEqual examples', () => {
// 区分 undefined 和缺失属性
expect({ a: undefined }).not.toStrictEqual({});
expect({ a: undefined }).toEqual({});
// 区分稀疏数组
expect([, , 1]).not.toStrictEqual([undefined, undefined, 1]);
});
布尔值匹配器 #
toBeTruthy / toBeFalsy #
javascript
test('boolean matchers', () => {
// toBeTruthy - 检查是否为真值
expect(true).toBeTruthy();
expect(1).toBeTruthy();
expect('hello').toBeTruthy();
expect({}).toBeTruthy();
expect([]).toBeTruthy();
// toBeFalsy - 检查是否为假值
expect(false).toBeFalsy();
expect(0).toBeFalsy();
expect('').toBeFalsy();
expect(null).toBeFalsy();
expect(undefined).toBeFalsy();
expect(NaN).toBeFalsy();
});
toBeNull / toBeUndefined / toBeDefined #
javascript
test('null and undefined matchers', () => {
// toBeNull
expect(null).toBeNull();
expect(undefined).not.toBeNull();
// toBeUndefined
expect(undefined).toBeUndefined();
expect(null).not.toBeUndefined();
// toBeDefined
expect(null).toBeDefined();
expect(undefined).not.toBeDefined();
});
数字匹配器 #
相等和不等 #
javascript
test('number equality', () => {
expect(1 + 1).toBe(2);
expect(1 + 1).toEqual(2);
expect(1 + 1).not.toBe(3);
});
大小比较 #
javascript
test('number comparisons', () => {
const value = 10;
// 大于
expect(value).toBeGreaterThan(5);
expect(value).toBeGreaterThan(9);
// 大于等于
expect(value).toBeGreaterThanOrEqual(10);
expect(value).toBeGreaterThanOrEqual(9);
// 小于
expect(value).toBeLessThan(20);
expect(value).toBeLessThan(11);
// 小于等于
expect(value).toBeLessThanOrEqual(10);
expect(value).toBeLessThanOrEqual(11);
});
浮点数比较 #
javascript
test('floating point numbers', () => {
// 使用 toBeCloseTo 避免浮点精度问题
expect(0.1 + 0.2).toBeCloseTo(0.3);
expect(0.1 + 0.2).not.toBe(0.3); // 浮点精度问题
// 指定精度
expect(0.1 + 0.2).toBeCloseTo(0.3, 5); // 5位小数精度
expect(0.1 + 0.2).toBeCloseTo(0.3, 10); // 10位小数精度
});
字符串匹配器 #
完全匹配 #
javascript
test('string equality', () => {
expect('hello').toBe('hello');
expect('hello').toEqual('hello');
});
包含匹配 #
javascript
test('string contains', () => {
// toContain - 检查是否包含子字符串
expect('hello world').toContain('world');
expect('hello world').not.toContain('foo');
// toContainEqual - 检查数组中是否包含特定对象
expect([{ a: 1 }, { b: 2 }]).toContainEqual({ a: 1 });
});
正则匹配 #
javascript
test('string regex', () => {
// toMatch - 正则表达式匹配
expect('hello world').toMatch(/world/);
expect('hello world').toMatch(/hello/);
expect('hello world').toMatch(/^hello/);
expect('hello world').toMatch(/world$/);
expect('hello world').toMatch(/\s/);
});
字符串长度 #
javascript
test('string length', () => {
expect('hello').toHaveLength(5);
expect('hello world').toHaveLength(11);
});
数组匹配器 #
包含元素 #
javascript
test('array contains', () => {
const arr = [1, 2, 3, 'a', 'b'];
// toContain - 检查是否包含元素
expect(arr).toContain(1);
expect(arr).toContain('a');
expect(arr).not.toContain(4);
// toContainEqual - 检查是否包含匹配的对象
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
];
expect(users).toContainEqual({ id: 1, name: 'John' });
});
数组长度 #
javascript
test('array length', () => {
expect([1, 2, 3]).toHaveLength(3);
expect([]).toHaveLength(0);
});
数组元素验证 #
javascript
test('array elements', () => {
// toHaveLength - 检查长度
expect([1, 2, 3]).toHaveLength(3);
// toEqual - 深度比较
expect([1, 2, 3]).toEqual([1, 2, 3]);
// arrayContaining - 检查是否包含子集
expect([1, 2, 3, 4, 5]).toEqual(
expect.arrayContaining([1, 2, 3])
);
});
对象匹配器 #
属性匹配 #
javascript
test('object properties', () => {
const user = {
name: 'John',
age: 30,
email: 'john@example.com'
};
// toHaveProperty - 检查属性是否存在
expect(user).toHaveProperty('name');
expect(user).toHaveProperty('age');
expect(user).toHaveProperty('address.city'); // 嵌套属性
// 检查属性值
expect(user).toHaveProperty('name', 'John');
expect(user).toHaveProperty('age', 30);
});
对象匹配 #
javascript
test('object matching', () => {
const user = {
name: 'John',
age: 30,
email: 'john@example.com'
};
// objectContaining - 检查是否包含部分属性
expect(user).toEqual(
expect.objectContaining({
name: 'John',
age: 30
})
);
// 匹配任意值
expect(user).toEqual(
expect.objectContaining({
name: expect.any(String),
age: expect.any(Number)
})
);
});
键匹配 #
javascript
test('object keys', () => {
const obj = { a: 1, b: 2, c: 3 };
// 检查是否有特定数量的键
expect(Object.keys(obj)).toHaveLength(3);
});
异常匹配器 #
toThrow #
javascript
function throwError() {
throw new Error('Something went wrong');
}
function throwCustomError() {
throw new TypeError('Invalid type');
}
test('throwing errors', () => {
// 检查是否抛出错误
expect(throwError).toThrow();
expect(throwError).toThrow(Error);
// 检查错误消息
expect(throwError).toThrow('Something went wrong');
expect(throwError).toThrow(/wrong/);
// 检查错误类型
expect(throwCustomError).toThrow(TypeError);
expect(throwCustomError).toThrow('Invalid type');
});
异步异常 #
javascript
async function asyncError() {
throw new Error('Async error');
}
test('async errors', async () => {
// 使用 rejects
await expect(asyncError()).rejects.toThrow('Async error');
await expect(asyncError()).rejects.toThrow(Error);
// 使用 .rejects
await expect(Promise.reject('error')).rejects.toBe('error');
});
函数匹配器 #
调用验证 #
javascript
test('function calls', () => {
const mockFn = jest.fn();
mockFn('hello');
mockFn('world');
// 检查是否被调用
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
// 检查调用参数
expect(mockFn).toHaveBeenCalledWith('hello');
expect(mockFn).toHaveBeenLastCalledWith('world');
expect(mockFn).toHaveBeenNthCalledWith(1, 'hello');
expect(mockFn).toHaveBeenNthCalledWith(2, 'world');
});
返回值验证 #
javascript
test('return values', () => {
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
mockFn.mockReturnValueOnce(1);
expect(mockFn()).toBe(1); // 第一次返回 1
expect(mockFn()).toBe(42); // 之后返回 42
});
异步匹配器 #
Promise #
javascript
test('promise matchers', async () => {
// resolves
await expect(Promise.resolve('success')).resolves.toBe('success');
await expect(Promise.resolve({ data: 'test' })).resolves.toEqual({ data: 'test' });
// rejects
await expect(Promise.reject('error')).rejects.toBe('error');
await expect(Promise.reject(new Error('fail'))).rejects.toThrow('fail');
});
否定匹配器 #
使用 .not 进行否定断言:
javascript
test('negation', () => {
expect(1 + 1).not.toBe(3);
expect('hello').not.toContain('world');
expect([1, 2, 3]).not.toContain(4);
expect({ a: 1 }).not.toEqual({ b: 2 });
expect(null).not.toBeUndefined();
});
类型匹配器 #
any #
javascript
test('expect.any', () => {
// 匹配任何该类型的值
expect('hello').toEqual(expect.any(String));
expect(42).toEqual(expect.any(Number));
expect(true).toEqual(expect.any(Boolean));
expect(() => {}).toEqual(expect.any(Function));
expect({}).toEqual(expect.any(Object));
expect([]).toEqual(expect.any(Array));
});
anything #
javascript
test('expect.anything', () => {
// 匹配任何非 null 和 undefined 的值
expect('hello').toEqual(expect.anything());
expect(42).toEqual(expect.anything());
expect(null).not.toEqual(expect.anything());
expect(undefined).not.toEqual(expect.anything());
});
类型构造函数 #
javascript
test('constructor matching', () => {
class MyClass {}
const instance = new MyClass();
expect(instance).toBeInstanceOf(MyClass);
expect([]).toBeInstanceOf(Array);
expect({}).toBeInstanceOf(Object);
expect(() => {}).toBeInstanceOf(Function);
});
快照匹配器 #
toMatchSnapshot #
javascript
test('snapshot matching', () => {
const user = {
name: 'John',
age: 30,
email: 'john@example.com'
};
expect(user).toMatchSnapshot();
});
内联快照 #
javascript
test('inline snapshot', () => {
const user = { name: 'John' };
expect(user).toMatchInlineSnapshot(`
{
"name": "John"
}
`);
});
高级匹配器 #
扩展匹配器 #
javascript
// jest.setup.js
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () => `expected ${received} not to be within range ${floor} - ${ceiling}`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to be within range ${floor} - ${ceiling}`,
pass: false,
};
}
},
});
// 使用自定义匹配器
test('custom matcher', () => {
expect(5).toBeWithinRange(1, 10);
expect(15).not.toBeWithinRange(1, 10);
});
数量匹配 #
javascript
test('length assertions', () => {
// 字符串长度
expect('hello').toHaveLength(5);
// 数组长度
expect([1, 2, 3]).toHaveLength(3);
// 对象键数量
expect(Object.keys({ a: 1, b: 2 })).toHaveLength(2);
});
匹配器对照表 #
| 匹配器 | 描述 |
|---|---|
toBe |
严格相等(引用相等) |
toEqual |
深度相等 |
toStrictEqual |
严格深度相等 |
toBeTruthy |
真值 |
toBeFalsy |
假值 |
toBeNull |
null |
toBeUndefined |
undefined |
toBeDefined |
已定义 |
toBeGreaterThan |
大于 |
toBeGreaterThanOrEqual |
大于等于 |
toBeLessThan |
小于 |
toBeLessThanOrEqual |
小于等于 |
toBeCloseTo |
浮点数近似 |
toMatch |
正则匹配 |
toContain |
包含元素 |
toContainEqual |
包含相等对象 |
toHaveProperty |
有属性 |
toThrow |
抛出错误 |
toHaveBeenCalled |
被调用 |
toHaveBeenCalledTimes |
被调用次数 |
toHaveBeenCalledWith |
被调用参数 |
resolves |
Promise 成功 |
rejects |
Promise 失败 |
toMatchSnapshot |
快照匹配 |
toBeInstanceOf |
实例类型 |
最佳实践 #
选择合适的匹配器 #
javascript
// ✅ 好的做法
expect(obj).toEqual({ a: 1 }); // 对象比较
expect(arr).toContain(1); // 数组包含
expect(str).toMatch(/hello/); // 字符串匹配
// ❌ 不好的做法
expect(JSON.stringify(obj)).toBe(JSON.stringify({ a: 1 }));
expect(arr.indexOf(1) > -1).toBe(true);
expect(str.includes('hello')).toBe(true);
使用语义化断言 #
javascript
// ✅ 好的做法
expect(user).toBeDefined();
expect(items).toHaveLength(3);
expect(error).toThrow();
// ❌ 不好的做法
expect(user !== undefined).toBe(true);
expect(items.length === 3).toBe(true);
expect(() => { throw error }).toThrow();
下一步 #
现在你已经掌握了 Jest 的各种断言匹配器,接下来学习 测试生命周期 了解测试的设置和清理!
最后更新:2026-03-28