在編寫測試時,咱們一般須要檢查值是否知足某些條件,Jest中提供的expect容許你訪問不少「Matchers」,這些「匹配器」容許您驗證不一樣的東西。html
Jest中提供了以下的驗證方法:前端
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) .toBeCloseTo(number, numDigits) .toBeDefined() .toBeFalsy() .toBeGreaterThan(number) .toBeGreaterThanOrEqual(number) .toBeLessThan(number) .toBeLessThanOrEqual(number) .toBeInstanceOf(Class) .toBeNull() .toBeTruthy() .toBeUndefined() .toContain(item) .toContainEqual(item) .toEqual(value) .toHaveLength(number) .toMatch(regexpOrString) .toMatchObject(object) .toHaveProperty(keyPath, value) .toMatchSnapshot(propertyMatchers, snapshotName) .toStrictEqual(value) .toThrow(error) .toThrowErrorMatchingSnapshot()
下面咱們將介紹部分驗證的使用場景:git
expect(value)github
每當您但願測試一個值時,都會使用expect函數。你不多會調用expect自己。相反你將使用expect和「matcher」函數來斷言關於值的某些內容。更容易理解這個例子。假設您有一個方法bestLaCroixFlavor(),它應該返回字符串「柚子」。下面是測試方法:數組
test('the best flavor is grapefruit', () => { expect(bestLaCroixFlavor()).toBe('grapefruit'); });
在上面的case中,toBe是matcher函數。爲了幫助你測試不一樣的東西,Jest中有不少不一樣的matcher函數。promise
expect的參數應該是代碼生成的值,而匹配程序的任何參數都應該是正確的值。若是你將它們混合在一塊兒,那麼你的測試仍然能夠工做,可是失敗測試的錯誤消息看起來會很奇怪。app
expect.extend(matchers)框架
你可使用expect.extend將本身的matcher添加到Jest中。例如假設你正在測試一個 theory library,而且你常常斷言數字能夠被其餘數整除,你能夠把它抽象成toBeDivisibleBy matcher:dom
expect.extend({ toBeDivisibleBy(received, argument) { const pass = received % argument == 0; if (pass) { return { message: () => `expected ${received} not to be divisible by ${argument}`, pass: true, }; } else { return { message: () => `expected ${received} to be divisible by ${argument}`, pass: false, }; } }, }); test('even and odd numbers', () => { expect(100).toBeDivisibleBy(2); expect(101).not.toBeDivisibleBy(2); expect({apples: 6, bananas: 3}).toEqual({ apples: expect.toBeDivisibleBy(2), bananas: expect.not.toBeDivisibleBy(2), }); });
expect.extends還支持異步匹配器。異步匹配器返回一個promise,所以你須要等待返回的值。讓咱們使用一個示例matcher來講明它們的用法。咱們要實現一個很是類似的matcher,而不是toBeDivisibleBy,惟一的區別是可分割的數字將從外部源中提取。 異步
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(); });
匹配器應該返回帶有兩個鍵的對象(或對象的promise)。pass指示是否存在匹配,message提供了一個沒有參數的函數,在失敗時返回錯誤消息。所以當pass爲false時,當expect(x). yourmatcher()失敗時,消息應該返回錯誤消息。當pass爲true時,消息應該返回expect(x).no . yourmatcher()失敗時的錯誤消息。
這些輔助函數能夠在自定義匹配器中找到: this.isNot,返回一個布爾值,讓你知道這個匹配器是用否認的.not修飾符調用的,容許你翻轉斷言。
this.equals(a, b)
若是兩個對象具備相同的值(遞歸地),則返回true。
this.utils有不少有用的工具。utils主要由來自jest-matcher-utils的導出組成。最有用的是matcherHint、printExpected和printReceived,它們能夠很好地格式化錯誤消息。例如看看toBe matcher的實現:
const diff = require('jest-diff'); expect.extend({ toBe(received, expected) { const pass = Object.is(received, expected); const message = pass ? () => this.utils.matcherHint('.not.toBe') + '\n\n' + `Expected value to not be (using Object.is):\n` + ` ${this.utils.printExpected(expected)}\n` + `Received:\n` + ` ${this.utils.printReceived(received)}` : () => { const diffString = diff(expected, received, { expand: this.expand, }); return ( this.utils.matcherHint('.toBe') + '\n\n' + `Expected value to be (using Object.is):\n` + ` ${this.utils.printExpected(expected)}\n` + `Received:\n` + ` ${this.utils.printReceived(received)}` + (diffString ? `\n\nDifference:\n\n${diffString}` : '') ); }; return {actual: received, message, pass}; }, });
打印結果以下:
expect(received).toBe(expected) Expected value to be (using Object.is): "banana" Received: "apple"
當斷言失敗時,錯誤消息應該向用戶提供必要的儘量多的信號,以便用戶可以快速地解決問題。你應該編寫一個精確的失敗消息,以確保自定義斷言的用戶具備良好的開發經驗。
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)
匹配給定構造函數所建立的任何內容。你能夠在內部使用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)
匹配一個接收到的數組,該數組包含預期數組中的全部元素。也就是說預期數組是接收數組的子集。所以它匹配一個接收到的數組,該數組包含不屬於預期數組的元素。
你能夠用它代替文字的值: toEqual或toBeCalledWith
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)
驗證在測試期間調用了必定數量的斷言。在測試異步代碼時這一般頗有用,以便確保回調中的斷言確實被調用。
假設咱們有一個函數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.hasAssertions()
驗證在測試期間至少調用了一個斷言。在測試異步代碼時,這一般頗有用以便確保回調中的斷言確實被調用。
假設咱們有一些處理狀態的函數。prepareState調用一個狀態對象的回調,validateState運行在那個狀態對象上,waitOnState返回一個承諾,直到全部prepareState回調完成。咱們能夠用:
test('prepareState prepares a valid state', () => { expect.hasAssertions(); prepareState(state => { expect(validateState(state)).toBeTruthy(); }); return waitOnState(); });
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. objectcontains相反。
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.stringContaining.相反
describe('not.stringContaining', () => { const expected = 'Hello world!'; it('matches if the actual string does not contain the expected substring', () => { expect('How are you?').toEqual(expect.not.stringContaining(expected)); }); });
expect.not.stringMatching(string | regexp)
匹配不匹配預期regexp的接收字符串。它與expect.stringMatching.相反
describe('not.stringMatching', () => { const expected = /Hello world!/; it('matches if the actual string does not match the expected regex', () => { expect('How are you?').toEqual(expect.not.stringMatching(expected)); }); });
expect.objectContaining(object)
匹配遞歸地匹配預期屬性的任何接收對象。也就是說,預期對象是接收對象的子集。所以,它匹配所接收的對象,該對象包含不屬於預期對象的屬性。
與指望對象中的文字屬性值不一樣,您可使用matchers、expect.anything()等等。
假設咱們但願使用事件對象調用onPress函數,咱們須要驗證的是事件是否有event.x屬性和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.stringMatching(string | regexp)
匹配與預期regexp匹配的接收字符串。你能夠用它代替文字的值:
1. 在toEqual或toBeCalledWith
2. 匹配arraycontains中的元素
3. 匹配objectContaining
或者toMatchObject的
屬性
這個示例還展現瞭如何使用expect嵌套多個不對稱的匹配器。在expect.arrayContaining stringMatching。
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), ); }); });
.toBe(value)
toBe只是檢查一個值是否符合您的指望。它使用對象。是要檢查徹底相等。例如此代碼將驗證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'); }); });
.toEqual(value)
若是要檢查兩個對象是否具備相同的值,請使用. toequal。此matcher遞歸地檢查全部字段的相等性,而不是檢查對象標識——這也稱爲「深度相等」。例如,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); }); });
.toMatchObject(object)
使用. tomatchobject檢查一個JavaScript對象是否匹配一個對象的屬性子集。它將把接收到的對象與預期對象中沒有的屬性匹配起來。
您還能夠傳遞一個對象數組,在這種狀況下,只有當接收到的數組中的每一個對象(在上面描述的番茄對象意義中)與預期數組中的相應對象相匹配時,該方法纔會返回true。若是想要檢查兩個數組在它們的元素數量上是否匹配,而不是arrayinclude,這是很是有用的,由於它容許在接收的數組中添加額外的元素。
能夠將屬性匹配到值或匹配項。
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 arrays', () => { test('the number of elements must match exactly', () => { expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]); }); // .arrayContaining "matches a received array which contains elements that // are *not* in the expected array" test('.toMatchObject does not allow extra elements', () => { expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}]); }); 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}, ]); }); });
.toHaveProperty(keyPath ,value)
使用. tohaveproperty檢查在提供的引用keyPath中是否存在對象的屬性。要檢查對象中深度嵌套的屬性,可使用點表示法或包含深度引用的keyPath的數組。
可選地,你能夠提供一個值來檢查它是否等於目標對象的keyPath中的值。此matcher使用「深度相等」(如toEqual()))並遞歸地檢查全部字段的相等性。
下面的示例包含一個帶有嵌套屬性的houseForSale對象。咱們使用tohave屬性來檢查對象中各類屬性的存在性和值。
// 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, }, }; test('this house has my desired features', () => { // Simple 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']); });
文中列出的只是其中一部分,更多詳細信息請參考官網: https://facebook.github.io/jest/docs/en/expect.html#expectanything
1. 前端測試框架Jest系列教程 -- Matchers(匹配器)
2.前端測試框架Jest系列教程 -- Asynchronous(測試異步代碼)
3.前端測試框架Jest系列教程 -- Mock Functions(模擬器)