Expect 断言
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
编写测试时,经常需要验证值是否符合特定条件。expect 提供了一系列"匹配器",可帮助您验证不同类型的内容。
更多由 Jest 社区维护的匹配器,请查看 jest-extended。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
本页中的 TypeScript 示例仅在显式导入 Jest API 的情况下才能按文档所述正常工作:
import {expect, jest, test} from '@jest/globals';
有关如何设置 Jest 与 TypeScript 配合使用的详细信息,请查阅入门指南。
参数详解
- Expect 断言
- 修饰符
- 匹配器
.toBe(value).toHaveBeenCalled().toHaveBeenCalledTimes(number).toHaveBeenCalledWith(arg1, arg2, ...).toHaveBeenLastCalledWith(arg1, arg2, ...).toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....).toHaveReturned().toHaveReturnedTimes(number).toHaveReturnedWith(value).toHaveLastReturnedWith(value).toHaveNthReturnedWith(nthCall, value).toHaveLength(number).toHaveProperty(keyPath, value?).toBeCloseTo(number, numDigits?).toBeDefined().toBeFalsy().toBeGreaterThan(number | bigint).toBeGreaterThanOrEqual(number | bigint).toBeLessThan(number | bigint).toBeLessThanOrEqual(number | bigint).toBeInstanceOf(Class).toBeNull().toBeTruthy().toBeUndefined().toBeNaN().toContain(item).toContainEqual(item).toEqual(value).toMatch(regexp | string).toMatchObject(object).toMatchSnapshot(propertyMatchers?, hint?).toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot).toStrictEqual(value).toThrow(error?).toThrowErrorMatchingSnapshot(hint?).toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
- 非对称匹配器
expect.anything()expect.any(constructor)expect.arrayContaining(array)expect.not.arrayContaining(array)expect.closeTo(number, numDigits?)expect.objectContaining(object)expect.not.objectContaining(object)expect.stringContaining(string)expect.not.stringContaining(string)expect.stringMatching(string | regexp)expect.not.stringMatching(string | regexp)
- 断言计数
- 扩展工具
Expect 断言
expect(value)
每次需要测试某个值时都会用到 expect 函数。通常不会单独调用 expect,而是将 expect 与"匹配器"函数配合使用来断言值的特性。
通过示例更容易理解。假设您有个应返回字符串 'grapefruit' 的方法 bestLaCroixFlavor(),测试代码如下:
test('the best flavor is grapefruit', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});
这里的 toBe 就是匹配器函数。下文将介绍多种不同的匹配器函数,用于验证各类情况。
expect 的参数应是代码生成的值,匹配器的参数应是正确值。如果混淆顺序,测试仍能运行,但失败时的错误信息会显得混乱。
修饰符
.not
当您知道如何测试某个条件时,.not 可测试其相反情况。例如测试最佳 La Croix 口味不是椰子味:
test('the best flavor is not coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});
.resolves
使用 resolves 解包已兑现 Promise 的值,以便链式调用其他匹配器。若 Promise 被拒绝,则断言失败。
例如以下代码测试 Promise 是否兑现且结果为 'lemon':
test('resolves to lemon', () => {
// make sure to add a return statement
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
由于仍在测试 Promise,测试本质仍是异步的。因此需要通过返回解包后的断言告知 Jest 等待。
或结合 async/await 使用 .resolves:
test('resolves to lemon', async () => {
await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus');
});
.rejects
使用 .rejects 解包已拒绝 Promise 的原因,以便链式调用其他匹配器。若 Promise 兑现,则断言失败。
例如以下代码测试 Promise 是否因 'octopus' 原因被拒绝:
test('rejects to octopus', () => {
// make sure to add a return statement
return expect(Promise.reject(new Error('octopus'))).rejects.toThrow(
'octopus',
);
});
由于仍在测试 Promise,测试本质仍是异步的。因此需要通过返回解包后的断言告知 Jest 等待。
或结合 async/await 使用 .rejects:
test('rejects to octopus', async () => {
await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus');
});
匹配器
.toBe(value)
使用 .toBe 比较原始值或检查对象实例的引用一致性。它调用 Object.is 进行值比较,比 === 严格相等运算符更适合测试场景。
例如以下代码验证 can 对象的属性:
const can = {
name: 'pamplemousse',
ounces: 12,
};
describe('the can', () => {
test('has 12 ounces', () => {
expect(can.ounces).toBe(12);
});
test('has a sophisticated name', () => {
expect(can.name).toBe('pamplemousse');
});
});
不要用 .toBe 比较浮点数。例如在 JavaScript 中由于舍入误差,0.2 + 0.1 并不严格等于 0.3。处理浮点数时请改用 .toBeCloseTo。
尽管 .toBe 匹配器检查的是引用一致性,但当断言失败时,它会报告值的深度差异对比。如果属性差异无法帮助理解测试失败原因(特别是当报告内容冗长时),可将比较逻辑移至 expect 函数内部。例如,要断言元素是否为相同实例:
-
将
expect(received).toBe(expected)改写为expect(Object.is(received, expected)).toBe(true) -
将
expect(received).not.toBe(expected)改写为expect(Object.is(received, expected)).toBe(false)
.toHaveBeenCalled()
别名:.toBeCalled()
使用 .toHaveBeenCalled 确保模拟函数已被调用。
例如,假设存在 drinkAll(drink, flavour) 函数,该函数接收 drink 方法并将其应用于所有可用饮料。可通过以下测试用例验证 drink 是否被调用:
function drinkAll(callback, flavour) {
if (flavour !== 'octopus') {
callback(flavour);
}
}
describe('drinkAll', () => {
test('drinks something lemon-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'lemon');
expect(drink).toHaveBeenCalled();
});
test('does not drink something octopus-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'octopus');
expect(drink).not.toHaveBeenCalled();
});
});
.toHaveBeenCalledTimes(number)
别名:.toBeCalledTimes(number)
使用 .toHaveBeenCalledTimes 确保模拟函数被精确调用指定次数。
例如,假设存在 drinkEach(drink, Array<flavor>) 函数,该函数接收 drink 方法并将其应用于传入的饮料数组。您可能想检查 drink 方法被调用的确切次数,可以通过以下测试套件实现:
test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});
.toHaveBeenCalledWith(arg1, arg2, ...)
别名:.toBeCalledWith()
使用 .toHaveBeenCalledWith 确保模拟函数被调用时包含特定参数。参数检查算法与 .toEqual 相同。
例如,假设可通过 register 函数注册饮料,且 applyToAll(f) 应将函数 f 应用于所有已注册饮料。验证逻辑如下:
test('registration applies correctly to orange La Croix', () => {
const beverage = new LaCroix('orange');
register(beverage);
const f = jest.fn();
applyToAll(f);
expect(f).toHaveBeenCalledWith(beverage);
});
.toHaveBeenLastCalledWith(arg1, arg2, ...)
别名:.lastCalledWith(arg1, arg2, ...)
对模拟函数使用 .toHaveBeenLastCalledWith 可检测其最后一次调用的参数。例如,假设 applyToAllFlavors(f) 函数会将 f 应用于多种口味,需验证其最后处理的口味是否为 'mango':
test('applying to all flavors does mango last', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
别名:.nthCalledWith(nthCall, arg1, arg2, ...)
对模拟函数使用 .toHaveBeenNthCalledWith 可检测其第 n 次调用的参数。例如,假设 drinkEach(drink, Array<flavor>) 函数会将 f 应用于多种口味,需验证其第一次处理的是 'lemon',第二次是 'octopus':
test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenNthCalledWith(1, 'lemon');
expect(drink).toHaveBeenNthCalledWith(2, 'octopus');
});
nth 参数必须是大于等于 1 的正整数。
.toHaveReturned()
别名:.toReturn()
对模拟函数使用 .toHaveReturned 可检测该函数是否成功返回(即未抛出错误)至少一次。例如,设模拟函数 drink 返回 true,可编写:
test('drinks returns', () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned();
});
.toHaveReturnedTimes(number)
别名:.toReturnTimes(number)
使用 .toHaveReturnedTimes 确保模拟函数成功返回(即未抛出错误)精确次数。任何抛出错误的调用不计入成功返回次数。
例如,设模拟函数 drink 返回 true,可编写:
test('drink returns twice', () => {
const drink = jest.fn(() => true);
drink();
drink();
expect(drink).toHaveReturnedTimes(2);
});
.toHaveReturnedWith(value)
别名:.toReturnWith(value)
使用 .toHaveReturnedWith 确保模拟函数返回了特定值。
例 如,假设您有一个模拟函数 drink 用于返回已饮用饮品的名称。您可以这样编写测试:
test('drink returns La Croix', () => {
const beverage = {name: 'La Croix'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage);
expect(drink).toHaveReturnedWith('La Croix');
});
.toHaveLastReturnedWith(value)
别名:.lastReturnedWith(value)
使用 .toHaveLastReturnedWith 测试模拟函数最后一次返回的特定值。如果最后一次调用抛出错误,无论提供的预期返回值是什么,该匹配器都会失败。
例如,假设您有一个模拟函数 drink 用于返回已饮用饮品的名称。您可以这样编写测试:
test('drink returns La Croix (Orange) last', () => {
const beverage1 = {name: 'La Croix (Lemon)'};
const beverage2 = {name: 'La Croix (Orange)'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage1);
drink(beverage2);
expect(drink).toHaveLastReturnedWith('La Croix (Orange)');
});
.toHaveNthReturnedWith(nthCall, value)
别名:.nthReturnedWith(nthCall, value)
使用 .toHaveNthReturnedWith 测试模拟函数第 n 次调用返回的特定值。如果第 n 次调用抛出错误,无论提供的预期返回值是什么,该匹配器都会失败。
例如,假设您有一个模拟函数 drink 用于返回已饮用饮品的名称。您可以这样编写测试:
test('drink returns expected nth calls', () => {
const beverage1 = {name: 'La Croix (Lemon)'};
const beverage2 = {name: 'La Croix (Orange)'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage1);
drink(beverage2);
expect(drink).toHaveNthReturnedWith(1, 'La Croix (Lemon)');
expect(drink).toHaveNthReturnedWith(2, 'La Croix (Orange)');
});
nth 参数必须是大于等于 1 的正整数。
.toHaveLength(number)
使用 .toHaveLength 检查对象是否具有 .length 属性且其值等于特定数字。
这在检查数组或字符串长度时特别有用。
expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);
.toHaveProperty(keyPath, value?)
使用 .toHaveProperty 检查对象在指定路径 keyPath 上是否存在属性。检查深层嵌套属性时,可使用点号表示法或包含深引用路径的数组。
可提供可选参数 value 来比较接收到的属性值(递归比较对象实例的所有属性,即深比较,与 toEqual 匹配器行为相同)。
以下示例包含具有嵌套属性的 houseForSale 对象。我们使用 toHaveProperty 检查该对象中各种属性的存在性和值:
// Object containing house features to be tested
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
'nice.oven': true,
},
livingroom: {
amenities: [
{
couch: [
['large', {dimensions: [20, 20]}],
['small', {dimensions: [10, 10]}],
],
},
],
},
'ceiling.height': 2,
};
test('this house has my desired features', () => {
// Example Referencing
expect(houseForSale).toHaveProperty('bath');
expect(houseForSale).toHaveProperty('bedrooms', 4);
expect(houseForSale).not.toHaveProperty('pool');
// Deep referencing using dot notation
expect(houseForSale).toHaveProperty('kitchen.area', 20);
expect(houseForSale).toHaveProperty('kitchen.amenities', [
'oven',
'stove',
'washer',
]);
expect(houseForSale).not.toHaveProperty('kitchen.open');
// Deep referencing using an array containing the keyPath
expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20);
expect(houseForSale).toHaveProperty(
['kitchen', 'amenities'],
['oven', 'stove', 'washer'],
);
expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven');
expect(houseForSale).toHaveProperty(
'livingroom.amenities[0].couch[0][1].dimensions[0]',
20,
);
expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']);
expect(houseForSale).not.toHaveProperty(['kitchen', 'open']);
// Referencing keys with dot in the key itself
expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall');
});
.toBeCloseTo(number, numDigits?)
使用 toBeCloseTo 比较浮点数的近似相等性。
可选参数 numDigits 限定小数点后检查的位数。默认值 2 的测试标准为 Math.abs(expected - received) < 0.005(即 10 ** -2 / 2)。
直观的相等比较常会失败,因为十进制(base 10)值的算术运算在有限精度的二进制(base 2)表示中常存在舍入误差。例如以下测试会失败:
test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBe(0.3); // Fails!
});
失败原因是 JavaScript 中 0.2 + 0.1 实际等于 0.30000000000000004。
例如,以下测试在 5 位精度下通过:
test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
由于浮点误差是 toBeCloseTo 要解决的问题,它不支持大整数值。
.toBeDefined()
使用 .toBeDefined 检查变量是否已定义。例如要检查函数 fetchNewFlavorIdea() 是否返回了有效值,可编写:
test('there is a new flavor idea', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});
虽然可以写 expect(fetchNewFlavorIdea()).not.toBe(undefined),但最佳实践是避免在代码中直接引用 undefined。
.toBeFalsy()
当不关心具体值,但需要确保值在布尔上下文中为假时,使用 .toBeFalsy。例如您的应用代码可能如下:
drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}
你可能并不关心 getErrors 具体返回什么值——它可能返回 false、null 或 0,而你的代码仍能正常工作。因此,如果你想测试饮用 La Croix 后没有错误发生,可以这样写:
test('drinking La Croix does not lead to errors', () => {
drinkSomeLaCroix();
expect(getErrors()).toBeFalsy();
});
在 JavaScript 中,有六个假值:false、0、''、null、undefined 和 NaN。其他所有值都是真值。
.toBeGreaterThan(number | bigint)
使用 toBeGreaterThan 比较 received > expected 对于数字或大整数值。例如,测试 ouncesPerCan() 返回的值是否超过 10 盎司:
test('ounces per can is more than 10', () => {
expect(ouncesPerCan()).toBeGreaterThan(10);
});
.toBeGreaterThanOrEqual(number | bigint)
使用 toBeGreaterThanOrEqual 比较数字或大整数值是否满足 received >= expected。例如,测试 ouncesPerCan() 返回的值是否至少为 12 盎司:
test('ounces per can is at least 12', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(12);
});
.toBeLessThan(number | bigint)
使用 toBeLessThan 比较数字或大整数值是否满足 received < expected。例如,测试 ouncesPerCan() 返回的值是否小于 20 盎司:
test('ounces per can is less than 20', () => {
expect(ouncesPerCan()).toBeLessThan(20);
});
.toBeLessThanOrEqual(number | bigint)
使用 toBeLessThanOrEqual 比较数字或大整数值是否满足 received <= expected。例如,测试 ouncesPerCan() 返回的值是否不超过 12 盎司:
test('ounces per can is at most 12', () => {
expect(ouncesPerCan()).toBeLessThanOrEqual(12);
});
.toBeInstanceOf(Class)
使用 .toBeInstanceOf(Class) 检查对象是否为类的实例。此匹配器底层使用 instanceof 实现。
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws
.toBeNull()
.toBeNull() 效果等同于 .toBe(null),但错误信息更友好。因此当需要检查某个值是否为 null 时,请使用 .toBeNull()。
function bloop() {
return null;
}
test('bloop returns null', () => {
expect(bloop()).toBeNull();
});
.toBeTruthy()
当你不在意具体值内容,只需确保在布尔上下文中值为真时,使用 .toBeTruthy。例如,假设你有如下应用代码:
drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}
你可能并不关心 thirstInfo 具体返回什么——它可能返回 true 或复杂对象,而你的代码仍能正常工作。因此若想测试饮用 La Croix 后 thirstInfo 将返回真值,可以这样写:
test('drinking La Croix leads to having thirst info', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});
在 JavaScript 中,有六个假值:false、0、''、null、undefined 和 NaN。其他所有值都是真值。
.toBeUndefined()
使用 .toBeUndefined 检查变量是否为 undefined。例如,若需验证函数 bestDrinkForFlavor(flavor) 对 'octopus' 口味返回 undefined(因为不存在好喝的章鱼口味饮料):
test('the best drink for octopus flavor is undefined', () => {
expect(bestDrinkForFlavor('octopus')).toBeUndefined();
});
虽然可以写 expect(bestDrinkForFlavor('octopus')).toBe(undefined),但最佳实践是避免直接在代码中引用 undefined。
.toBeNaN()
检查值是否为 NaN 时使用 .toBeNaN。
test('passes when value is NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});
.toContain(item)
当需要检查数组中是否包含某项时使用 .toContain。此匹配器使用严格相等 === 检测数组元素。.toContain 也可用于检测字符串是否包含子串。
例如,若 getAllFlavors() 返回口味数组,且你想确认其中包含 lime,可以这样写:
test('the flavor list contains lime', () => {
expect(getAllFlavors()).toContain('lime');
});