Jest expect API
在编写测试时,经常需要检查值是否满足特定条件。expect可以访问许多匹配器(Matchers),这些匹配器让你验证不同的事物。
有关 Jest 社区维护的其他 Jest 匹配器,请查看jest-extended。
方法
- expect(value)
- expect.extend(matchers)
- expect.anything()
- expect.any(constructor)
- expect.arrayContaining(array)
- expect.assertions(number)
- expect.hasAssertions()
- expect.not.arrayContaining(array)
- expect.not.objectContaining(object)
- expect.not.stringContaining(string)
- expect.not.stringMatching(string | regexp)
- expect.objectContaining(object)
- expect.stringContaining(string)
- expect.stringMatching(string | regexp)
- expect.addSnapshotSerializer(serializer)
- .not
- .resolves
- .rejects
- .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(regexpOrString)
- .toMatchObject(object)
- .toMatchSnapshot(propertyMatchers?, hint?)
- .toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)
- .toStrictEqual(value)
- .toThrow(error?)
- .toThrowErrorMatchingSnapshot(hint?)
- .toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
参考
expect(value)
每次要测试一个值时都会使用expect
函数。 你很少会自己调用expect
。 相反,你会使用expect
和“matcher”函数来断言有关值的内容。
很容易理解这一点的一个例子。 假设你有一个方法bestLaCroixFlavor()
,它应该返回字符串'grapefruit'
。 以下是如何测试:
test('the best flavor is grapefruit', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});
在这种情况下,toBe
是匹配器功能。有许多不同的匹配器函数,记录如下,来帮助你测试不同的东西。
expect
的参数应该是代码产生的值,匹配器的任何参数应该是正确的值。 如果混合使用,测试仍然可以工作,但是失败测试的错误信息将会显得奇怪。
expect.extend(matchers)
可以使用expect.extend
将自己的匹配器添加到Jest。 例如,假设你正在测试一个数字实用程序库,并且经常断言数字出现在其他数字的特定范围内。可以将其抽象为toBeWithinRange
匹配器:
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('numeric ranges', () => {
expect(100).toBeWithinRange(90, 110);
expect(101).not.toBeWithinRange(0, 100);
expect({apples: 6, bananas: 3}).toEqual({
apples: expect.toBeWithinRange(1, 10),
bananas: expect.not.toBeWithinRange(11, 20),
});
});
注意:在 TypeScript 中,@types/jest
例如使用时,可以toBeWithinRange
像这样声明新的匹配器:
declare global {
namespace jest {
interface Matchers<R> {
toBeWithinRange(a: number, b: number): R;
}
}
}
异步匹配器
expect.extend
还支持异步匹配器。异步匹配器返回一个 Promise,因此你需要等待返回的值。让我们使用一个示例匹配器来说明它们的用法。我们将实现一个名为 的匹配器toBeDivisibleByExternalValue
,从外部源中提取可整除数。
expect.extend({
async toBeDivisibleByExternalValue(received) {
const externalValue = await getExternalValueFromRemoteSource();
const pass = received % externalValue == 0;
if (pass) {
return {
message: () =>
`expected ${received} not to be divisible by ${externalValue}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${received} to be divisible by ${externalValue}`,
pass: false,
};
}
},
});
test('is divisible by external value', async () => {
await expect(100).toBeDivisibleByExternalValue();
await expect(101).not.toBeDivisibleByExternalValue();
});
自定义匹配器 API
匹配器应该返回带有两个键的对象(或对象的承诺)。pass
指示是否存在匹配项,message
提供的函数不带参数,在失败时返回错误消息。 因此,当pass
为false时,message
应该返回当expect(x).yourMatcher()
失败时的错误消息。 而当pass
为true时, message
应该返回当expect(x).not.yourMatcher()
失败时的错误信息。
匹配器使用传递给expect(x)
的参数和传递给的参数调用.yourMatcher(y, z)
:
expect.extend({
yourMatcher(x, y, z) {
return {
pass: true,
message: () => '',
};
},
});
这些辅助函数和属性可以this
在自定义匹配器中找到:
this.isNot
一个布尔值,让你知道此匹配器是使用否定.not
修饰符调用的,允许显示清晰正确的匹配器提示(请参阅示例代码)。
this.promise
一个字符串,允许显示清晰正确的匹配器提示:
-
'rejects'
如果使用 promise.rejects
修饰符调用 matcher
-
'resolves'
如果使用 promise.resolves
修饰符调用 matcher
-
''
如果没有使用承诺修饰符调用匹配器
this.equals(a, b)
这是一个深度相等的函数,如果两个对象具有相同的值(递归),则返回true
。
this.expand
一个布尔值,让你知道这个匹配器是用一个expand
选项调用的。当使用--expand
标志调用 Jest 时,this.expand
可用于确定 Jest 是否应显示完整的差异和错误。
this.utils
在this.utils
上有一些有用的工具,主要由jest-matcher-utils导出。
最有用的是matcherHint
,printExpected
和printReceived
很好地格式化错误消息。例如,看一下toBe
匹配器的实现:
const diff = require('jest-diff');
expect.extend({
toBe(received, expected) {
const options = {
comment: 'Object.is equality',
isNot: this.isNot,
promise: this.promise,
};
const pass = Object.is(received, expected);
const message = pass
? () =>
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
`Expected: not ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`
: () => {
const diffString = diff(expected, received, {
expand: this.expand,
});
return (
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
(diffString && diffString.includes('- Expect')
? `Difference:\n\n${diffString}`
: `Expected: ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`)
);
};
return {actual: received, message, pass};
},
});
这将打印如下内容:
expect(received).toBe(expected)
Expected value to be (using Object.is):
"banana"
Received:
"apple"
当断言失败时,错误消息应向用户提供尽可能多的信号,以便他们能够快速解决问题。应该制作精确的失败消息,以确保你的自定义断言的用户拥有良好的开发人员体验。
自定义快照匹配器
要在自定义匹配器中使用快照测试,可以jest-snapshot
从匹配器中导入和使用它。
这是一个快照匹配器,它修剪字符串以存储给定长度,.toMatchTrimmedSnapshot(length)
:
const {toMatchSnapshot} = require('jest-snapshot');
expect.extend({
toMatchTrimmedSnapshot(received, length) {
return toMatchSnapshot.call(
this,
received.substring(0, length),
'toMatchTrimmedSnapshot',
);
},
});
it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10);
});
/*
Stored snapshot will look like:
exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`;
*/
也可以为内联快照创建自定义匹配器,快照将正确添加到自定义匹配器中。但是,当第一个参数是属性匹配器时,内联快照将始终尝试附加到第一个参数或第二个参数,因此无法在自定义匹配器中接受自定义参数。
const {toMatchInlineSnapshot} = require('jest-snapshot');
expect.extend({
toMatchTrimmedInlineSnapshot(received) {
return toMatchInlineSnapshot.call(this, received.substring(0, 10));
},
});
it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(
`"extra long"`
);
*/
});
expect.anything()
expect.anything()
匹配除null
或 之外的任何内容undefined
。可以在内部toEqual
或toBeCalledWith
代替文字值使用它。例如,如果要检查是否使用非空参数调用了模拟函数:
test('map calls its argument with a non-null argument', () => {
const mock = jest.fn();
[1].map(x => mock(x));
expect(mock).toBeCalledWith(expect.anything());
});
expect.any(constructor)
expect.any(constructor)
匹配使用给定构造函数创建的任何内容。可以在内部toEqual
或toBeCalledWith
代替文字值使用它。例如,如果要检查是否使用数字调用了模拟函数:
function randocall(fn) {
return fn(Math.floor(Math.random() * 6 + 1));
}
test('randocall calls its callback with a number', () => {
const mock = jest.fn();
randocall(mock);
expect(mock).toBeCalledWith(expect.any(Number));
});
expect.arrayContaining(array)
expect.arrayContaining(array)
匹配接收到的数组,该数组包含预期数组中的所有元素。也就是说,预期数组是接收数组的子集。因此,它匹配包含不在预期数组中的元素的接收数组。
可以使用它代替文字值:
- 在
toEqual
或toBeCalledWith
- 匹配
objectContaining
或toMatchObject
describe('arrayContaining', () => {
const expected = ['Alice', 'Bob'];
it('matches even if received contains additional elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
it('does not match if received does not contain expected elements', () => {
expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected));
});
});
describe('Beware of a misunderstanding! A sequence of dice rolls', () => {
const expected = [1, 2, 3, 4, 5, 6];
it('matches even with an unexpected number 7', () => {
expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match without an expected number 2', () => {
expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual(
expect.arrayContaining(expected),
);
});
});
expect.assertions(number)
expect.assertions(number)
验证在测试期间调用了一定数量的断言。这在测试异步代码时通常很有用,以确保回调中的断言确实被调用。
例如,假设我们有一个函数doAsync
接收两个回调callback1
和callback2
,它将以未知顺序异步调用它们。我们可以用以下方法测试:
test('doAsync calls both callbacks', () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
doAsync(callback1, callback2);
});
该expect.assertions(2)
调用确保两个回调都被实际调用。
expect.hasAssertions()
expect.hasAssertions()
验证在测试期间至少调用了一个断言。这在测试异步代码时通常很有用,以确保回调中的断言确实被调用。
例如,假设我们有一些处理状态的函数。prepareState
使用状态对象调用回调,validateState
在该状态对象上运行,并waitOnState
返回一个等待所有prepareState
回调完成的承诺。我们可以用以下方法测试:
test('prepareState prepares a valid state', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});
该expect.hasAssertions()
调用确保prepareState
回调实际被调用。
expect.not.arrayContaining(array)
expect.not.arrayContaining(array)
匹配接收到的数组,该数组不包含预期数组中的所有元素。也就是说,预期数组不是接收数组的子集。
它是expect.arrayContaining
的倒数。
describe('not.arrayContaining', () => {
const expected = ['Samantha'];
it('matches if the actual array does not contain the expected elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(
expect.not.arrayContaining(expected),
);
});
});
expect.not.objectContaining(object)
expect.not.objectContaining(object)
匹配任何不递归匹配预期属性的接收对象。也就是说,预期对象不是接收对象的子集。因此,它匹配包含不在预期对象中的属性的接收对象。
它是expect.objectContaining
的倒数。
describe('not.objectContaining', () => {
const expected = {foo: 'bar'};
it('matches if the actual object does not contain expected key: value pairs', () => {
expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected));
});
});
expect.not.stringContaining(string)
expect.not.stringContaining(string)
如果它不是字符串或者它是一个不包含确切预期字符串的字符串,则匹配接收到的值。
它是expect.stringContaining
的倒数。
describe('not.stringContaining', () => {
const expected = 'Hello world!';
it('matches if the received value does not contain the expected substring', () => {
expect('How are you?').toEqual(expect.not.stringContaining(expected));
});
});
expect.not.stringMatching(string | regexp)
expect.not.stringMatching(string | regexp)
如果接收到的值不是字符串,或者它是与预期字符串或正则表达式不匹配的字符串,则匹配接收到的值。
它是expect.stringMatching
的倒数。
describe('not.stringMatching', () => {
const expected = /Hello world!/;
it('matches if the received value does not match the expected regex', () => {
expect('How are you?').toEqual(expect.not.stringMatching(expected));
});
});
expect.objectContaining(object)
expect.objectContaining(object)
匹配任何接收到的递归匹配预期属性的对象。也就是说,预期对象是接收对象的子集。因此,它匹配包含存在于预期对象中的属性的接收对象。
可以使用匹配器、 等expect.anything()
,而不是预期对象中的文字属性值。
例如,假设我们希望onPress
用一个Event
对象调用一个函数,我们需要验证的是该事件是否具有event.x
和event.y
属性。我们可以这样做:
test('onPress gets called with the right thing', () => {
const onPress = jest.fn();
simulatePresses(onPress);
expect(onPress).toBeCalledWith(
expect.objectContaining({
x: expect.any(Number),
y: expect.any(Number),
}),
);
});
expect.stringContaining(string)
expect.stringContaining(string)
如果它是包含确切预期字符串的字符串,则匹配接收到的值。
expect.stringMatching(string | regexp)
expect.stringMatching(string | regexp)
如果它是与预期字符串或正则表达式匹配的字符串,则匹配接收到的值。
可以使用它代替文字值:
- 在
toEqual
或toBeCalledWith
- 匹配一个元素
arrayContaining
- 匹配
objectContaining
或toMatchObject
这个例子还展示了如何嵌套多个非对称匹配器,expect.stringMatching
在expect.arrayContaining
.
describe('stringMatching in arrayContaining', () => {
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
it('matches even if received contains additional elements', () => {
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match if received does not contain expected elements', () => {
expect(['Roberto', 'Evelina']).not.toEqual(
expect.arrayContaining(expected),
);
});
});
expect.addSnapshotSerializer(serializer)
可以调用expect.addSnapshotSerializer
以添加格式化应用程序特定数据结构的模块。
对于单个测试文件,添加的模块位于snapshotSerializers
配置中的任何模块之前,后者位于内置 JavaScript 类型和 React 元素的默认快照序列化程序之前。添加的最后一个模块是测试的第一个模块。
import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);
// affects expect(value).toMatchSnapshot() assertions in the test file
如果在单个测试文件中添加快照序列化程序而不是将其添加到snapshotSerializers配置中:
- 使依赖显式而不是隐式。
- 避免了可能导致从create-react-app 中弹出的配置限制。
有关更多信息,请参阅配置 Jest。
.not
如果你知道如何测试某样东西,.not
让你测试它的反面。例如,此代码测试最好的 La Croix
风味不是椰子:
test('the best flavor is not coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});
.resolves
使用.resolves
解开一个兑现承诺的价值,所以任何其他匹配可以链接。如果承诺被拒绝,则断言失败。
例如,此代码测试 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
是否以 reason
拒绝'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
与浮点数一起使用。例如,由于四舍五入,在 JavaScript0.2 + 0.1中并不严格等于0.3。如果有浮点数,请尝试.toBeCloseTo
。
尽管.toB
e匹配器检查引用标识,但如果断言失败,它会报告值的深度比较。如果属性之间的差异不能帮助你理解测试失败的原因,尤其是在报告很大的情况下,那么你可以将比较移到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
for 'lemon'
,而不是 for 'octopus'
,因为'octopus'
味道真的很奇怪,为什么会有章鱼味的东西?你可以用这个测试套件做到这一点:
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
函数接受一个函数并将其应用于传递的饮料数组。你可能想要检查饮料函数被调用的确切次数。你可以用这个测试套件做到这一点:
test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});
.toHaveBeenCalledWith(arg1, arg2, ...)
同样在别名下: .toBeCalledWith()
使用.toHaveBeenCalledWith
以确保模拟函数被调用的具体参数。
例如,假设你可以使用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
用来测试它被调用的参数。例如,假设你有一个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');
});
注意:第 n 个参数必须是从 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)');
});
注意:第 n 个参数必须是从 1 开始的正整数。
.toHaveLength(number)
使用.toHaveLength
检查的对象有一个.length
属性,并将其设置为某一数值。
这对于检查数组或字符串大小特别有用。
expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);
.toHaveProperty(keyPath, value?)
使用.toHaveProperty
检查,如果在提供的参考属性keyPath
存在的对象。为了检查对象中深度嵌套的属性,你可以使用点表示法或包含用于深度引用的 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,
},
'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(['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)
。
直观的相等比较经常失败,因为十进制(基数 10)值的算术通常在有限精度的二进制(基数 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中,有六个falsy值: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)
但错误消息更好一些。所以.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中,有六个falsy值:false
,0
,''
,null
,undefined
,和NaN
。其他一切都是真实的。
.toBeUndefined()
使用.toBeUndefined
检查变量不确定。例如,如果你想检查一个函数bestDrinkForFlavor(flavor)
返回undefined
的'octopus'
味道,因为没有好的octopus-flavored
饮料:
test('the best drink for octopus flavor is undefined', () => {
expect(bestDrinkForFlavor('octopus')).toBeUndefined();
});
你可以编写expect(bestDrinkForFlavor('octopus')).toBe(undefined)
,但最好避免undefined
在代码中直接引用。
.toBeNaN()
.toBeNaN
在检查值时使用NaN
。
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');
});
.toContainEqual(item)
使用.toContainEqual
时要检查是否具有特定结构和值的项目包含在一个阵列。为了测试数组中的项目,这个匹配器递归地检查所有字段的相等性,而不是检查对象身份。
describe('my beverage', () => {
test('is delicious and not sour', () => {
const myBeverage = {delicious: true, sour: false};
expect(myBeverages()).toContainEqual(myBeverage);
});
});
.toEqual(value)
用于.toEqual
递归比较对象实例的所有属性(也称为“深度”相等)。它调用Object.is比较原始值,这比===
严格相等运算符更适合测试。
例如,.toEqual
和.toBe
不同的表现在这个测试套件,所以所有的测试都通过了:
const can1 = {
flavor: 'grapefruit',
ounces: 12,
};
const can2 = {
flavor: 'grapefruit',
ounces: 12,
};
describe('the La Croix cans on my desk', () => {
test('have all the same properties', () => {
expect(can1).toEqual(can2);
});
test('are not the exact same can', () => {
expect(can1).not.toBe(can2);
});
});
注意:.toEqual
不会对两个错误执行深度相等检查。仅message
考虑 Error
的属性是否相等。建议使用.toThrow
匹配器进行错误测试。
如果属性之间的差异不能帮助你理解测试失败的原因,尤其是在报告很大的情况下,那么你可以将比较移到expect函数中。例如,使用类的equals
方法Buffer
来断言缓冲区是否包含相同的内容:
- 重写
expect(received).toEqual(expected)为expect(received.equals(expected)).toBe(true)
- 重写
expect(received).not.toEqual(expected)为expect(received.equals(expected)).toBe(false)
.toMatch(regexpOrString)
使用.toMatch
检查字符串中的正则表达式匹配。
例如,你可能不知道究竟essayOnTheBestFlavor()
返回什么,但你知道它是一个非常长的字符串,并且子字符串grapefruit
应该在某个地方。可以使用以下方法进行测试:
describe('an essay on the best flavor', () => {
test('mentions grapefruit', () => {
expect(essayOnTheBestFlavor()).toMatch(/grapefruit/);
expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));
});
});
这个匹配器还接受一个字符串,它将尝试匹配:
describe('grapefruits are healthy', () => {
test('grapefruits are a fruit', () => {
expect('grapefruits').toMatch('fruit');
});
});
.toMatchObject(object)
使用.toMatchObject
检查JavaScript对象对象的属性的子集相匹配。它会将接收到的对象与不在预期对象中的属性进行匹配。
还可以传递一个对象数组,在这种情况下,只有当接收到的数组中的每个对象都匹配(在上述toMatchObject意义上)预期数组中的相应对象时,该方法才会返回 true
。如果你想检查两个数组的元素数量是否匹配,这很有用,而不是arrayContaining
允许接收数组中的额外元素。
可以将属性与值或匹配器进行匹配。
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
},
};
const desiredHouse = {
bath: true,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
wallColor: expect.stringMatching(/white|yellow/),
},
};
test('the house has my desired features', () => {
expect(houseForSale).toMatchObject(desiredHouse);
});
describe('toMatchObject applied to arrays', () => {
test('the number of elements must match exactly', () => {
expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]);
});
test('.toMatchObject is called for each elements, so extra object properties are okay', () => {
expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([
{foo: 'bar'},
{baz: 1},
]);
});
});
.toMatchSnapshot(propertyMatchers?, hint?)
这可确保值与最近的快照相匹配。查看快照测试指南了解更多信息。
可以提供一个可选的propertyMatchers
对象参数,如果接收的值是对象实例,则该参数具有不对称匹配器作为预期属性子集的值。这就像toMatchObject
对属性的子集使用灵活的标准,然后是快照测试作为其余属性的精确标准。
可以提供hint
附加到测试名称的可选字符串参数。尽管 Jest 总是在快照名称的末尾附加一个数字,但在区分单个或块中的多个快照时,简短的描述性提示可能比数字更有用。Jest 在相应文件中按名称对快照进行排序。 ittest.snap
.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)
确保值与最近的快照相匹配。
可以提供一个可选的propertyMatchers
对象参数,如果接收的值是对象实例,则该参数具有不对称匹配器作为预期属性子集的值。这就像toMatchObject
对属性的子集使用灵活的标准,然后是快照测试作为其余属性的精确标准。
Jest在第一次运行测试时将inlineSnapshot
字符串参数添加到测试文件(而不是外部.snap
文件)中的匹配器。
查看内联快照部分了解更多信息。
.toStrictEqual(value)
使用.toStrictEqual
测试的对象具有相同的类型以及结构。
与以下的区别.toEqual
:
- undefined检查具有属性的键。例如,使用时
{a: undefined, b: 2}
不匹配。{b: 2}.toStrictEqual
- 检查数组稀疏性。例如,使用时
[, 1]
不匹配。[undefined, 1].toStrictEqual
- 检查对象类型是否相等。例如,具有字段
a
和的类实例b
将不等于具有字段a
和的文字对象b
。
class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}
describe('the La Croix cans on my desk', () => {
test('are not semantically the same', () => {
expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'});
expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'});
});
});
.toThrow(error?)
同样在别名下: .toThrowError(error?)
使用.toThrow
测试,当它被称为函数抛出。例如,如果我们要测试drinkFlavor('octopus')
投掷,因为章鱼的味道太难喝了,我们可以这样写:
test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
注意:必须将代码包裹在一个函数中,否则无法捕获错误,断言失败。
可以提供一个可选参数来测试是否抛出了特定错误:
- 正则表达式:错误消息与模式匹配
- 字符串:错误消息包括子字符串
- 错误对象:错误消息等于对象的消息属性
- 错误类:错误对象是类的实例
例如,假设它drinkFlavor
是这样编码的:
function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}
我们可以通过几种方式测试这个错误是否被抛出:
test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}
// Test that the error message says "yuck" somewhere: these are equivalent
expect(drinkOctopus).toThrowError(/yuck/);
expect(drinkOctopus).toThrowError('yuck');
// Test the exact error message
expect(drinkOctopus).toThrowError(/^yuck, octopus flavor$/);
expect(drinkOctopus).toThrowError(new Error('yuck, octopus flavor'));
// Test that we get a DisgustingFlavorError
expect(drinkOctopus).toThrowError(DisgustingFlavorError);
});
.toThrowErrorMatchingSnapshot(hint?)
使用.toThrowErrorMatchingSnapshot
测试一个函数抛出匹配最新的快照时,它被称为一个错误。
可以提供hint附加到测试名称的可选字符串参数。尽管 Jest 总是在快照名称的末尾附加一个数字,但在区分单个或块中的多个快照时,简短的描述性提示可能比数字更有用。Jest 在相应文件中按名称对快照进行排序。 ittest.snap
例如,假设你有一个drinkFlavor
函数,该函数在风味为 时抛出'octopus'
,其编码如下:
function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}
此函数的测试将如下所示:
test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}
expect(drinkOctopus).toThrowErrorMatchingSnapshot();
});
它将生成以下快照:
exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`;
查看React Tree Snapshot Testing以获取有关快照测试的更多信息。
.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
使用.toThrowErrorMatchingInlineSnapshot
测试一个函数抛出匹配最新的快照时,它被称为一个错误。
Jest在第一次运行测试时将inlineSnapshot
字符串参数添加到测试文件(而不是外部.snap
文件)中的匹配器。
查看内联快照部分了解更多信息。