在編寫測試時,經(jīng)常需要檢查值是否滿足特定條件。expect可以訪問許多匹配器(Matchers),這些匹配器讓你驗證不同的事物。
有關(guān) Jest 社區(qū)維護的其他 Jest 匹配器,請查看jest-extended。
每次要測試一個值時都會使用?expect
?函數(shù)。 你很少會自己調(diào)用?expect
?。 相反,你會使用?expect
?和“matcher”函數(shù)來斷言有關(guān)值的內(nèi)容。
很容易理解這一點的一個例子。 假設(shè)你有一個方法?bestLaCroixFlavor()
?,它應(yīng)該返回字符串?'grapefruit'
?。 以下是如何測試:
test('the best flavor is grapefruit', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});
在這種情況下,?toBe
?是匹配器功能。有許多不同的匹配器函數(shù),記錄如下,來幫助你測試不同的東西。
?expect
?的參數(shù)應(yīng)該是代碼產(chǎn)生的值,匹配器的任何參數(shù)應(yīng)該是正確的值。 如果混合使用,測試仍然可以工作,但是失敗測試的錯誤信息將會顯得奇怪。
可以使用?expect.extend
?將自己的匹配器添加到Jest。 例如,假設(shè)你正在測試一個數(shù)字實用程序庫,并且經(jīng)常斷言數(shù)字出現(xiàn)在其他數(shù)字的特定范圍內(nèi)。可以將其抽象為?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,因此你需要等待返回的值。讓我們使用一個示例匹配器來說明它們的用法。我們將實現(xiàn)一個名為 的匹配器?toBeDivisibleByExternalValue
?,從外部源中提取可整除數(shù)。
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();
});
匹配器應(yīng)該返回帶有兩個鍵的對象(或?qū)ο蟮某兄Z)。?pass
?指示是否存在匹配項,?message
?提供的函數(shù)不帶參數(shù),在失敗時返回錯誤消息。 因此,當(dāng)?pass
?為false時,?message
?應(yīng)該返回當(dāng)?expect(x).yourMatcher()
?失敗時的錯誤消息。 而當(dāng)?pass
?為true時, ?message
?應(yīng)該返回當(dāng)?expect(x).not.yourMatcher()
?失敗時的錯誤信息。
匹配器使用傳遞給?expect(x)
?的參數(shù)和傳遞給的參數(shù)調(diào)用?.yourMatcher(y, z)
?:
expect.extend({
yourMatcher(x, y, z) {
return {
pass: true,
message: () => '',
};
},
});
這些輔助函數(shù)和屬性可以?this
?在自定義匹配器中找到:
一個布爾值,讓你知道此匹配器是使用否定?.not
?修飾符調(diào)用的,允許顯示清晰正確的匹配器提示(請參閱示例代碼)。
一個字符串,允許顯示清晰正確的匹配器提示:
'rejects'
?如果使用 ?promise.rejects
?修飾符調(diào)用 ?matcher
?'resolves'
?如果使用 ?promise.resolves
?修飾符調(diào)用 ?matcher
?''
? 如果沒有使用承諾修飾符調(diào)用匹配器這是一個深度相等的函數(shù),如果兩個對象具有相同的值(遞歸),則返回?true
?。
一個布爾值,讓你知道這個匹配器是用一個?expand
?選項調(diào)用的。當(dāng)使用?--expand
?標(biāo)志調(diào)用 Jest 時,?this.expand
?可用于確定 Jest 是否應(yīng)顯示完整的差異和錯誤。
在?this.utils
?上有一些有用的工具,主要由jest-matcher-utils導(dǎo)出。
最有用的是?matcherHint
?,?printExpected
?和?printReceived
?很好地格式化錯誤消息。例如,看一下?toBe
?匹配器的實現(xiàn):
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};
},
});
這將打印如下內(nèi)容:
expect(received).toBe(expected)
Expected value to be (using Object.is):
"banana"
Received:
"apple"
當(dāng)斷言失敗時,錯誤消息應(yīng)向用戶提供盡可能多的信號,以便他們能夠快速解決問題。應(yīng)該制作精確的失敗消息,以確保你的自定義斷言的用戶擁有良好的開發(fā)人員體驗。
要在自定義匹配器中使用快照測試,可以?jest-snapshot
?從匹配器中導(dǎo)入和使用它。
這是一個快照匹配器,它修剪字符串以存儲給定長度,?.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"`;
*/
也可以為內(nèi)聯(lián)快照創(chuàng)建自定義匹配器,快照將正確添加到自定義匹配器中。但是,當(dāng)?shù)谝粋€參數(shù)是屬性匹配器時,內(nèi)聯(lián)快照將始終嘗試附加到第一個參數(shù)或第二個參數(shù),因此無法在自定義匹配器中接受自定義參數(shù)。
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()
?匹配除?null
?或 之外的任何內(nèi)容?undefined
???梢栽趦?nèi)部?toEqual
?或?toBeCalledWith
?代替文字值使用它。例如,如果要檢查是否使用非空參數(shù)調(diào)用了模擬函數(shù):
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)
?匹配使用給定構(gòu)造函數(shù)創(chuàng)建的任何內(nèi)容??梢栽趦?nèi)部?toEqual
?或?toBeCalledWith
?代替文字值使用它。例如,如果要檢查是否使用數(shù)字調(diào)用了模擬函數(shù):
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)
?匹配接收到的數(shù)組,該數(shù)組包含預(yù)期數(shù)組中的所有元素。也就是說,預(yù)期數(shù)組是接收數(shù)組的子集。因此,它匹配包含不在預(yù)期數(shù)組中的元素的接收數(shù)組。
可以使用它代替文字值:
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)
?驗證在測試期間調(diào)用了一定數(shù)量的斷言。這在測試異步代碼時通常很有用,以確保回調(diào)中的斷言確實被調(diào)用。
例如,假設(shè)我們有一個函數(shù)?doAsync
?接收兩個回調(diào)?callback1
?和?callback2
?,它將以未知順序異步調(diào)用它們。我們可以用以下方法測試:
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)
?調(diào)用確保兩個回調(diào)都被實際調(diào)用。
?expect.hasAssertions()
?驗證在測試期間至少調(diào)用了一個斷言。這在測試異步代碼時通常很有用,以確?;卣{(diào)中的斷言確實被調(diào)用。
例如,假設(shè)我們有一些處理狀態(tài)的函數(shù)。?prepareState
?使用狀態(tài)對象調(diào)用回調(diào),?validateState
?在該狀態(tài)對象上運行,并?waitOnState
?返回一個等待所有?prepareState
?回調(diào)完成的承諾。我們可以用以下方法測試:
test('prepareState prepares a valid state', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});
該?expect.hasAssertions()
?調(diào)用確保?prepareState
?回調(diào)實際被調(diào)用。
?expect.not.arrayContaining(array)
?匹配接收到的數(shù)組,該數(shù)組不包含預(yù)期數(shù)組中的所有元素。也就是說,預(yù)期數(shù)組不是接收數(shù)組的子集。
它是?expect.arrayContaining
?的倒數(shù)??。
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)
?匹配任何不遞歸匹配預(yù)期屬性的接收對象。也就是說,預(yù)期對象不是接收對象的子集。因此,它匹配包含不在預(yù)期對象中的屬性的接收對象。
它是?expect.objectContaining
的倒數(shù)?。
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)
? 如果它不是字符串或者它是一個不包含確切預(yù)期字符串的字符串,則匹配接收到的值。
它是?expect.stringContaining
?的倒數(shù)??。
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)
? 如果接收到的值不是字符串,或者它是與預(yù)期字符串或正則表達式不匹配的字符串,則匹配接收到的值。
它是?expect.stringMatching
?的倒數(shù)。
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)
?匹配任何接收到的遞歸匹配預(yù)期屬性的對象。也就是說,預(yù)期對象是接收對象的子集。因此,它匹配包含存在于預(yù)期對象中的屬性的接收對象。
可以使用匹配器、 等?expect.anything()
?,而不是預(yù)期對象中的文字屬性值。
例如,假設(shè)我們希望?onPress
?用一個?Event
?對象調(diào)用一個函數(shù),我們需要驗證的是該事件是否具有?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)
? 如果它是包含確切預(yù)期字符串的字符串,則匹配接收到的值。
?expect.stringMatching(string | regexp)
? 如果它是與預(yù)期字符串或正則表達式匹配的字符串,則匹配接收到的值。
可以使用它代替文字值:
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),
);
});
});
可以調(diào)用?expect.addSnapshotSerializer
?以添加格式化應(yīng)用程序特定數(shù)據(jù)結(jié)構(gòu)的模塊。
對于單個測試文件,添加的模塊位于?snapshotSerializers
?配置中的任何模塊之前,后者位于內(nèi)置 JavaScript 類型和 React 元素的默認(rèn)快照序列化程序之前。添加的最后一個模塊是測試的第一個模塊。
import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);
// affects expect(value).toMatchSnapshot() assertions in the test file
如果在單個測試文件中添加快照序列化程序而不是將其添加到snapshotSerializers配置中:
有關(guān)更多信息,請參閱配置 Jest。
如果你知道如何測試某樣?xùn)|西,?.not
?讓你測試它的反面。例如,此代碼測試最好的 ?La Croix
? 風(fēng)味不是椰子:
test('the best flavor is not coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});
使用?.??resolves??
?解開一個兌現(xiàn)承諾的價值,所以任何其他匹配可以鏈接。如果承諾被拒絕,則斷言失敗。
例如,此代碼測試 ?promise
?是否已解析并且結(jié)果值為?'lemon'
?:
test('resolves to lemon', () => {
// make sure to add a return statement
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
請注意,由于仍在測試 promise,因此測試仍然是異步的。因此,需要通過返回未包裝的斷言來告訴 Jest 等待。
或者,可以?async
?/?await
?結(jié)合使用?.resolves
?:
test('resolves to lemon', async () => {
await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus');
});
使用?.rejects
?拆開包裝,因此任何其他匹配可鏈接被拒絕承諾的理由。如果承諾被實現(xiàn),則斷言失敗。
例如,此代碼測試 ?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
?比較原始值或檢查對象實例的引用一致性。它調(diào)用?Object.is
?比較值,這比?===
?嚴(yán)格相等運算符更適合測試。
例如,此代碼將驗證?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
?與浮點數(shù)一起使用。例如,由于四舍五入,在 JavaScript0.2 + 0.1中并不嚴(yán)格等于0.3。如果有浮點數(shù),請嘗試?.toBeCloseTo
?。
盡管?.toB
?e匹配器檢查引用標(biāo)識,但如果斷言失敗,它會報告值的深度比較。如果屬性之間的差異不能幫助你理解測試失敗的原因,尤其是在報告很大的情況下,那么你可以將比較移到?expect
?函數(shù)中。例如,要斷言元素是否是同一個實例:
expect(received).toBe(expected)為expect(Object.is(received, expected)).toBe(true)
?expect(received).not.toBe(expected)為expect(Object.is(received, expected)).toBe(false)
?同樣在別名下:? .toBeCalled()
?
使用?.toHaveBeenCalled
?以確保模擬功能得到調(diào)用。
例如,假設(shè)你有一個?drinkAll(drink, flavour)
?函數(shù),它接受一個?drink
?函數(shù)并將其應(yīng)用于所有可用的飲料。你可能想檢查是否??
?調(diào)用了 ?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();
});
});
同樣在別名下:? .toBeCalledTimes(number)
?
使用?.toHaveBeenCalledTimes
?以確保模擬功能得到調(diào)用次數(shù)確切數(shù)字。
例如,假設(shè)你有一個?drinkEach(drink, Array<flavor>)
?函數(shù),drink
?函數(shù)接受一個函數(shù)并將其應(yīng)用于傳遞的飲料數(shù)組。你可能想要檢查飲料函數(shù)被調(diào)用的確切次數(shù)。你可以用這個測試套件做到這一點:
test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});
同樣在別名下:? .toBeCalledWith()
?
使用?.toHaveBeenCalledWith
?以確保模擬函數(shù)被調(diào)用的具體參數(shù)。
例如,假設(shè)你可以使用register函數(shù)注冊飲料,并且?applyToAll(f)
?應(yīng)該將該函數(shù)f應(yīng)用于所有已注冊的飲料。為了確保這有效,你可以寫:
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);
});
同樣在別名下:? .lastCalledWith(arg1, arg2, ...)
?
如果你有一個模擬函數(shù),你可以?.toHaveBeenLastCalledWith
?用來測試它最后被調(diào)用的參數(shù)。例如,假設(shè)你有一個?applyToAllFlavors(f)
?適用f于一系列風(fēng)味的函數(shù),并且你希望確保在調(diào)用它時,它所操作的最后一種風(fēng)味是?'mango'
?。你可以寫:
test('applying to all flavors does mango last', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});
同樣在別名下:? .nthCalledWith(nthCall, arg1, arg2, ...)
?
如果你有一個模擬函數(shù),你可以?.toHaveBeenNthCalledWith
?用來測試它被調(diào)用的參數(shù)。例如,假設(shè)你有一個?drinkEach(drink, Array<flavor>)
?適用?f
?于一系列風(fēng)味的函數(shù),并且你希望確保在調(diào)用它時,它操作的第一個風(fēng)味是?'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 個參數(shù)必須是從 1 開始的正整數(shù)。
同樣在別名下:? .toReturn()
?
如果你有一個模擬函數(shù),你可以?.toHaveReturned
?用來測試模擬函數(shù)是否成功返回(即沒有拋出錯誤)至少一次。例如,假設(shè)你有一個?drink
?返回?true
?. 你可以寫:
test('drinks returns', () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned();
});
同樣在別名下:? .toReturnTimes(number)
?
使用?.toHaveReturnedTimes
?以確保模擬函數(shù)返回成功(即未引發(fā)錯誤)的次一個確切的數(shù)字。任何拋出錯誤的模擬函數(shù)調(diào)用都不會計入函數(shù)返回的次數(shù)。
例如,假設(shè)你有一個?drink
?返回?true
?. 你可以寫:
test('drink returns twice', () => {
const drink = jest.fn(() => true);
drink();
drink();
expect(drink).toHaveReturnedTimes(2);
});
同樣在別名下: ?.toReturnWith(value)
?
使用?.toHaveReturnedWith
?以確保模擬函數(shù)返回的特定值。
例如,假設(shè)你有一個?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');
});
同樣在別名下:? .lastReturnedWith(value)
?
使用?.toHaveLastReturnedWith
?來測試一個模擬函數(shù)的最后一個返回的特定值。如果對模擬函數(shù)的最后一次調(diào)用拋出錯誤,則無論提供什么值作為預(yù)期返回值,此匹配器都將失敗。
例如,假設(shè)你有一個?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)');
});
同樣在別名下:? .nthReturnedWith(nthCall, value)
?
使用?.toHaveNthReturnedWith
?測試,對于第?n
?個調(diào)用返回一個模擬功能的具體價值。如果對模擬函數(shù)的第 ?n
?次調(diào)用拋出錯誤,則無論你提供什么值作為預(yù)期返回值,此匹配器都將失敗。
例如,假設(shè)你有一個?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 個參數(shù)必須是從 1 開始的正整數(shù)。
使用?.toHaveLength
?檢查的對象有一個?.length
?屬性,并將其設(shè)置為某一數(shù)值。
這對于檢查數(shù)組或字符串大小特別有用。
expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);
使用?.toHaveProperty
?檢查,如果在提供的參考屬性?keyPath
?存在的對象。為了檢查對象中深度嵌套的屬性,你可以使用點表示法或包含用于深度引用的 ?keyPath
?的數(shù)組。
你可以提供一個可選?value
?參數(shù)來比較接收到的屬性值(遞歸地用于對象實例的所有屬性,也稱為深度相等,如?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
?浮點數(shù)的近似相等比較。
可選?numDigits
?參數(shù)限制小數(shù)點后要檢查的位數(shù)。對于默認(rèn)值2,測試標(biāo)準(zhǔn)是?Math.abs(expected - received) < 0.005(即10 ** -2 / 2)
?。
直觀的相等比較經(jīng)常失敗,因為十進制(基數(shù) 10)值的算術(shù)通常在有限精度的二進制(基數(shù) 2)表示中存在舍入誤差。例如,此測試失?。?/p>
test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBe(0.3); // Fails!
});
它失敗了,因為在 JavaScript 中,0.2 + 0.1實際上是0.30000000000000004.
例如,此測試以 5 位數(shù)的精度通過:
test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
因為浮點錯誤是要?toBeCloseTo
?解決的問題,所以它不支持大整數(shù)值。
使用?.toBeDefined
?檢查一個變量是不是不確定的。例如,如果你想檢查一個函數(shù)是否?fetchNewFlavorIdea()
?返回了一些東西,你可以這樣寫:
test('there is a new flavor idea', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});
可以編寫?expect(fetchNewFlavorIdea()).not.toBe(undefined)
?,但最好避免undefined在代碼中直接引用。
使用?.toBeFalsy
?時,你不關(guān)心的值是什么,你要確保一個值是在布爾上下文假的。例如,假設(shè)你有一些如下所示的應(yīng)用程序代碼:
drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}
你可能并不關(guān)心?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
?比較?received
?> ?expected
?的數(shù)量或大整數(shù)值。例如,?ouncesPerCan()
?返回值大于 10 盎司的測試:
test('ounces per can is more than 10', () => {
expect(ouncesPerCan()).toBeGreaterThan(10);
});
用?toBeGreaterThanOrEqual
?比較?received
?>= ?expected
?的數(shù)量或大整數(shù)值。例如,?ouncesPerCan()
?返回至少 12 盎司值的測試:
test('ounces per can is at least 12', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(12);
});
用?toBeLessThan
?比較?received
?< ?expected
?的數(shù)量或大整數(shù)值。例如,?ouncesPerCan()
?返回小于 20 盎司值的測試:
test('ounces per can is less than 20', () => {
expect(ouncesPerCan()).toBeLessThan(20);
});
用?toBeLessThanOrEqual
?比較?received
?<= ?expected
?的數(shù)量或大整數(shù)值。例如,測試?ouncesPerCan()
?最多返回 12 盎司的值:
test('ounces per can is at most 12', () => {
expect(ouncesPerCan()).toBeLessThanOrEqual(12);
});
使用?.toBeInstanceOf(Class)
?檢查對象是一個類的實例。這個匹配器?instanceof
?在下面使用。
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws
.?toBeNull()
?與相同?.toBe(null)
?但錯誤消息更好一些。所以?.toBeNull()
?當(dāng)你想檢查某些東西是否為空時使用。
function bloop() {
return null;
}
test('bloop returns null', () => {
expect(bloop()).toBeNull();
});
使用?.toBeTruthy
?時,你不關(guān)心的值是什么,你要確保一個值在布爾環(huán)境是真實的。例如,假設(shè)你有一些如下所示的應(yīng)用程序代碼:
drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}
你可能不在乎?thirstInfo
?返回什么,?特別是
?它可能返回true或者一個復(fù)雜的對象,并且你的代碼仍然可以工作。所以如果你想測試在喝了一些 ?La Croix
? 后?thirstInfo
?是否真實,你可以寫:
test('drinking La Croix leads to having thirst info', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});
在JavaScript中,有六個falsy值:?false
?,?0
?,?''
?,?null
?,?undefined
?,和?NaN
?。其他一切都是真實的。
使用?.toBeUndefined
?檢查變量不確定。例如,如果你想檢查一個函數(shù)?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
?在檢查值時使用?NaN
?。
test('passes when value is NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});
使用?.toContain
?時要檢查的項目是在數(shù)組中。為了測試數(shù)組中的項目,這使用?===
?了嚴(yán)格的相等性檢查。.toContain還可以檢查一個字符串是否是另一個字符串的子字符串。
例如,如果?getAllFlavors()
?返回一個風(fēng)味數(shù)組,并且你想確保其中包含該數(shù)組,?lime
?則可以編寫:
test('the flavor list contains lime', () => {
expect(getAllFlavors()).toContain('lime');
});
使用?.toContainEqual
?時要檢查是否具有特定結(jié)構(gòu)和值的項目包含在一個陣列。為了測試數(shù)組中的項目,這個匹配器遞歸地檢查所有字段的相等性,而不是檢查對象身份。
describe('my beverage', () => {
test('is delicious and not sour', () => {
const myBeverage = {delicious: true, sour: false};
expect(myBeverages()).toContainEqual(myBeverage);
});
});
用于?.toEqual
?遞歸比較對象實例的所有屬性(也稱為“深度”相等)。它調(diào)用Object.is比較原始值,這比?===
?嚴(yán)格相等運算符更適合測試。
例如,?.toEqual
?和?.toBe
?不同的表現(xiàn)在這個測試套件,所以所有的測試都通過了:
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
?不會對兩個錯誤執(zhí)行深度相等檢查。僅?message
?考慮 ?Error
?的屬性是否相等。建議使用?.toThrow
?匹配器進行錯誤測試。
如果屬性之間的差異不能幫助你理解測試失敗的原因,尤其是在報告很大的情況下,那么你可以將比較移到expect函數(shù)中。例如,使用類的?equals
?方法?Buffer
?來斷言緩沖區(qū)是否包含相同的內(nèi)容:
expect(received).toEqual(expected)為expect(received.equals(expected)).toBe(true)
?expect(received).not.toEqual(expected)為expect(received.equals(expected)).toBe(false)
?使用?.toMatch
?檢查字符串中的正則表達式匹配。
例如,你可能不知道究竟?essayOnTheBestFlavor()
?返回什么,但你知道它是一個非常長的字符串,并且子字符串?grapefruit
?應(yīng)該在某個地方??梢允褂靡韵路椒ㄟM行測試:
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
?檢查JavaScript對象對象的屬性的子集相匹配。它會將接收到的對象與不在預(yù)期對象中的屬性進行匹配。
還可以傳遞一個對象數(shù)組,在這種情況下,只有當(dāng)接收到的數(shù)組中的每個對象都匹配(在上述toMatchObject意義上)預(yù)期數(shù)組中的相應(yīng)對象時,該方法才會返回 ?true
?。如果你想檢查兩個數(shù)組的元素數(shù)量是否匹配,這很有用,而不是?arrayContaining
?允許接收數(shù)組中的額外元素。
可以將屬性與值或匹配器進行匹配。
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},
]);
});
});
這可確保值與最近的快照相匹配。查看快照測試指南了解更多信息。
可以提供一個可選的?propertyMatchers
?對象參數(shù),如果接收的值是對象實例,則該參數(shù)具有不對稱匹配器作為預(yù)期屬性子集的值。這就像?toMatchObject
?對屬性的子集使用靈活的標(biāo)準(zhǔn),然后是快照測試作為其余屬性的精確標(biāo)準(zhǔn)。
可以提供?hint
?附加到測試名稱的可選字符串參數(shù)。盡管 Jest 總是在快照名稱的末尾附加一個數(shù)字,但在區(qū)分單個或塊中的多個快照時,簡短的描述性提示可能比數(shù)字更有用。Jest 在相應(yīng)文件中按名稱對快照進行排序。 ?ittest.snap
?
確保值與最近的快照相匹配。
可以提供一個可選的?propertyMatchers
?對象參數(shù),如果接收的值是對象實例,則該參數(shù)具有不對稱匹配器作為預(yù)期屬性子集的值。這就像?toMatchObject
?對屬性的子集使用靈活的標(biāo)準(zhǔn),然后是快照測試作為其余屬性的精確標(biāo)準(zhǔn)。
Jest在第一次運行測試時將?inlineSnapshot
?字符串參數(shù)添加到測試文件(而不是外部?.snap
?文件)中的匹配器。
查看內(nèi)聯(lián)快照部分了解更多信息。
使用?.toStrictEqual
?測試的對象具有相同的類型以及結(jié)構(gòu)。
與以下的區(qū)別?.toEqual
?:
{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'});
});
});
同樣在別名下:? .toThrowError(error?)
?
使用?.toThrow
?測試,當(dāng)它被稱為函數(shù)拋出。例如,如果我們要測試?drinkFlavor('octopus')
?投擲,因為章魚的味道太難喝了,我們可以這樣寫:
test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
注意:必須將代碼包裹在一個函數(shù)中,否則無法捕獲錯誤,斷言失敗。
可以提供一個可選參數(shù)來測試是否拋出了特定錯誤:
例如,假設(shè)它?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
?測試一個函數(shù)拋出匹配最新的快照時,它被稱為一個錯誤。
可以提供hint附加到測試名稱的可選字符串參數(shù)。盡管 Jest 總是在快照名稱的末尾附加一個數(shù)字,但在區(qū)分單個或塊中的多個快照時,簡短的描述性提示可能比數(shù)字更有用。Jest 在相應(yīng)文件中按名稱對快照進行排序。 ittest.snap
例如,假設(shè)你有一個?drinkFlavor
?函數(shù),該函數(shù)在風(fēng)味為 時拋出?'octopus'
?,其編碼如下:
function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}
此函數(shù)的測試將如下所示:
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以獲取有關(guān)快照測試的更多信息。
使用?.toThrowErrorMatchingInlineSnapshot
?測試一個函數(shù)拋出匹配最新的快照時,它被稱為一個錯誤。
Jest在第一次運行測試時將?inlineSnapshot
?字符串參數(shù)添加到測試文件(而不是外部?.snap
?文件)中的匹配器。
查看內(nèi)聯(lián)快照部分了解更多信息。
更多建議: