Jest Expect: Comprehensive Guide to Matchers and Extensions
Often times you need to check that values meet certain conditions when you are writing tests. expect provides you with a number of ?matchers? that enable you to validate different things.
Methods
- 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)
- .toBeGreaterThanOrEqual(number)
- .toBeLessThan(number)
- .toBeLessThanOrEqual(number)
- .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)
Reference
expect(value)
The expect function is used whenever you want to test a value. Rarely will you call expect by itself. Instead, you use expect along with a "matcher" function so as to assert something about a value.
An example will make this easier to understand. If you have a method bestLaCroixFlavor() which is to return the string 'grapefruit'. The code to test that is given below:
test('grapefruit is the best flavor', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});
The matcher function here is toBe. Different matcher functions exist, and to help you test different things, we have documented them below:
The argument to expect has to be the value that your code produces, and any argument to the matcher has to be the correct value. Your code will still work if you mix them up, but the error messages that you get on failing tests will look strange.
expect.extend(matchers)
expect.extend can be used to add your own matchers to Jest. For instance, when you're testing a number utility library and you are frequently asserting that numbers appear within particular ranges of other numbers. You can abstract that into a toBeWithinRange matcher:
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),
});
});
Note: In TypeScript, when you are using @types/jest for example, you will be able to declare the new toBeWithinRange matcher like this:
declare global {
namespace jest {
interface Matchers<R> {
toBeWithinRange(a: number, b: number): R;
}
}
}
Async Matchers
Async matchers are also supported by expect.extend. Async matchers will return a Promise so you need to await the returned value. We will use an example matcher to illustrate their usage. We will be implementing a matcher called toBeDivisibleByExternalValue, where the divisible number will be pulled from an external source.
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();
});
Custom Matchers API
Matchers has to return an object (or a Promise of an object) with two keys. pass will indicate whether there was a match or not, and message will provide a function with no arguments that returns an error message in case of failure. Thus, if pass is false, message will have to return the error message for when expect(x).yourMatcher() fails. And if pass is true, message has to return the error message for when expect(x).not.yourMatcher() fails.
We call matchers with the argument passed to expect(x) followed by the arguments passed to .yourMatcher(y, z):
expect.extend({
yourMatcher(x, y, z) {
return {
pass: true,
message: () => '',
};
},
});
These helper properties and functions can be found on this inside a custom matcher:
this.isNot
A boolean to let you know this matcher was called with the negated .not modifier, this allows you to flip your assertion and display a correct and clear matcher hint (as shown in the example above).
this.promise
This is a string that allows you to display a clear and correct matcher hint:
- 'rejects' if matcher was called using the promise .rejects modifier
- 'resolves' if matcher was called using the promise .resolves modifier
- '' if matcher was not called using a promise modifier
this.equals(a, b)
This is a deep-equality function that returns true if two objects have the same values (recursively).
this.expand
A boolean that lets you know this matcher was called with an expand option. When you call Jest with the --expand flag, this.expand may be used to determine if Jest is expected to show full diffs and errors.
this.utils
There are a number of helpful tools that are exposed on this.utils, these primarily consist of the exports from jest-matcher-utils.
matcherHint, printExpected and printReceived are the most useful to format the error messages nicely. For instance, consider this implementation for the toBe matcher:
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: ${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};
},
});
This prints something like this:
expect(received).toBe(expected)
Expected value to be (using Object.is):
"banana"
Received:
"apple"`
If an assertion fails, the error message will give as much signal as necessary to the user so that they can resolve their issue quickly. You need to craft a precise failure message to make sure users of your custom assertions have a good developer experience.
Custom snapshot matchers
If you want to use snapshot testing inside of your custom matcher you can import jest-snapshot and then use it from within your matcher.
Here's a simple snapshot matcher that will trim a string to store for a given length, .toMatchTrimmedSnapshot(length):
const {toMatchSnapshot} = require('jest-snapshot');
expect.extend({
toMatchTrimmedSnapshot(received, length) {
return toMatchSnapshot.call(
this,
received.substring(0, length),
'toMatchTrimmedSnapshot',
);
},
});
it('will store only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10);
});
/*
Stored snapshot looks like:
exports[`will store only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`;
*/
expect.anything()
expect.anything() will match anything except null or undefined. It can be used inside toEqual or toBeCalledWith rather than a literal value. For instance, if you want to check whether a mock function is called with a non-null argument:
test('map will call 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) will match anything that was created with the given constructor. It can be used inside toEqual or toBeCalledWith rather than a literal value. For instance, if you want to check whether a mock function is called with a number:
function randocall(fn) {
return fn(Math.floor(Math.random() * 6 + 1));
}
test('randocall will call its callback with a number', () => {
const mock = jest.fn();
randocall(mock);
expect(mock).toBeCalledWith(expect.any(Number));
});
expect.arrayContaining(array)
expect.arrayContaining(array) will match a received array which contains all of the elements in the expected array. That is, the expected array will be subset of the received array. Therefore, it will match a received array which contains elements that are not in the expected array.
It can be used instead of a literal value:
- in toEqual or toBeCalledWith
- to match a property in objectContaining or toMatchObject
describe('arrayContaining', () => {
const expected = ['Alice', 'Bob'];
it('will match even if received contains additional elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
it('will 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 will roll', () => {
const expected = [1, 2, 3, 4, 5, 6];
it('will match even with an unexpected number 7', () => {
expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
expect.arrayContaining(expected),
);
});
it('will 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) will verify that a certain number of assertions are called during a test. Often, this is useful when testing asynchronous code, so as to make sure that assertions in a callback actually got called.
For instance, let us say that we have a function doAsync which receives two callbacks callback1 and callback2, it asynchronously calls both of them in an unknown order. This can be tested with:
test('doAsync will call both callbacks', () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
doAsync(callback1, callback2);
});
The expect.assertions(2) call will ensure that both callbacks actually get called.
expect.hasAssertions()
expect.hasAssertions() will verify that at least one assertion is called during a test. This is often useful when you are testing asynchronous code, in order to make sure that the assertions in a callback actually got called.
For instance, let us say that we have a few functions that all deal with state. prepareState will call a callback with a state object, validateState will run on that state object, and waitOnState will return a promise that will wait until all prepareState callbacks complete. We can test this using:
test('prepareState will prepare a valid state', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});
The expect.hasAssertions() call will ensure that the prepareState callback actually gets called.
expect.not.arrayContaining(array)
expect.not.arrayContaining(array) will match a received array which does not contain all of the elements in the expected array. That is, the expected array is not a subset of the array that is received.
expect.not.arrayContaining is the inverse of expect.arrayContaining.
describe('not.arrayContaining', () => {
const expected = ['Samantha'];
it('will match 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) will match any received object that does not recursively match the expected properties. That is, the expected object is not a subset of the object that is received. Therefore, it will match a received object which contains properties that are not in the expected object.
It is the opposite of expect.objectContaining.
describe('not.objectContaining', () => {
const expected = {foo: 'bar'};
it('will match 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) will match the received value if it is not a string or if it is a string which does not contain the exact expected string.
It is the opposite of expect.stringContaining.
describe('not.stringContaining', () => {
const expected = 'Hello world!';
it('will match 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) will match the received value if it is not a string or if it is a string which does not match the expected string or regular expression.
It is the opposite of expect.stringMatching.
describe('not.stringMatching', () => {
const expected = /Hello world!/;
it('will match 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) will match any received object that recursively matches the expected properties. That is, the expected object is a subset of the object that is received. Therefore, it will match a received object which contains properties that are present in the expected object.
Rather than literal property values in the expected object, matchers, expect.anything() and so on can be used.
For instance, let us say that we expect an onPress function to be called with an Event object, and all that we need to verify is that the event has event.y and event.x properties. We can do that using:
test('onPress will be 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) will match the received value if it is a string that contains the exact expected string.
expect.stringMatching(string | regexp)
expect.stringMatching(string | regexp) will match the received value if it is a string that matches the expected string or regular expression.
You can use it rather than a literal value:
- in toEqual or toBeCalledWith
- to match an element in arrayContaining
- to match a property in objectContaining or toMatchObject
The example below will also show how you can nest multiple asymmetric matchers, using expect.stringMatching inside the expect.arrayContaining.
describe('stringMatching in arrayContaining', () => {
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
it('will match even if received contains additional elements', () => {
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(
expect.arrayContaining(expected),
);
});
it(will not match if received does not contain expected elements', () => {
expect(['Roberto', 'Evelina']).not.toEqual(
expect.arrayContaining(expected),
);
});
});
expect.addSnapshotSerializer(serializer)
You can call expect.addSnapshotSerializer to add a module that formats application-specific data structures.
Given an individual test file, an added module will precede any modules from snapshotSerializers configuration, this will precede the default snapshot serializers for built-in JavaScript types and for React elements. The last module added will be the first module tested.
import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);
// will affect expect(value).toMatchSnapshot() assertions in the test file
If you add a snapshot serializer in individual test files rather than adding it to snapshotSerializers configuration:
- You will make the dependency explicit instead of implicit.
- You will avoid limits to configuration that might cause you to eject from create-react-app.
.not
.not lets you test its opposite of what you already know how to test. For instance, the code below tests that the best La Croix flavor is not apple:
test('the best flavor is not apple', () => {
expect(bestLaCroixFlavor()).not.toBe('apple');
});
.resolves
You should use resolves to unwrap the value of a fulfilled promise so any other matcher can be chained. If the promise is rejected, the assertion will fail.
For instance, the code below tests that the promise resolves and that the resulting value is 'lemon':
test('resolves to lemon', () => {
// makes sure to add a return statement
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
Note, the test is still asynchronous since we are still testing promises. Hence, you have to tell Jest to wait by returning the unwrapped assertion.
Alternatively, you can combine async/await with .resolves:
test('will resolve to lemon', async () => {
await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus');
});
.rejects
You should use .rejects to unwrap the reason of a rejected promise so any other matcher can be chained. If the promise is fulfilled, the assertion will fail.
For instance, the code below tests that the promise rejects with reason 'squid':
test('rejects to squid', () => {
// makes sure to add a return statement
return expect(Promise.reject(new Error('squid'))).rejects.toThrow(
'squid',
);
});
Note, the test is asynchronous, since we are still testing promises. Hence, you need to tell Jest to wait by returning the unwrapped assertion.
Alternatively, you can combine async/await with .rejects.
test('rejects to squid', async () => {
await expect(Promise.reject(new Error('squid'))).rejects.toThrow('squid');
});
.toBe(value)
You should use .toBe to compare primitive values or if you want to check referential identity of object instances. It will call Object.is to compare values, which is even better for testing compared to === strict equality operator.
For instance, the code below validates some properties of the can object:
const can = {
name: 'pamplemousse',
ounces: 12,
};
describe('the can', () => {
test('has 12 ounces', () => {
expect(can.ounces).toBe(12);
});
test('it has a sophisticated name', () => {
expect(can.name).toBe('pamplemousse');
});
});
You should not use .toBe with floating-point numbers. For instance, because of rounding, in JavaScript 0.2 + 0.1 is strictly not equal to 0.3. If you have floating point numbers, try .toBeCloseTo instead.
Although the .toBe matcher will check referential identity, it will report a deep comparison of values if the assertion fails. If differences between properties do not help you to understand why a test failed, especially for large report, then you can move the comparison into the expect function. For instance, to assert whether the elements are the same instance or not:
- expect(received).toBe(expected) is rewritten as expect(Object.is(received, expected)).toBe(true)
- expect(received).not.toBe(expected) is rewritten as expect(Object.is(received, expected)).toBe(false)
.toHaveBeenCalled()
This is also under the alias: .toBeCalled()
You should use .toHaveBeenCalled to ensure that a mock function got called.
For instance, let us say you have a drinkAll(drink, flavour) function which takes a drink function and then applies it to all available beverages. You might decide to check that drink gets called for 'apple', but not for 'squid', because 'squid' flavour is really weird and why would anything be squid-flavoured? You can do that this test suite below:
function drinkAll(callback, flavour) {
if (flavour !== 'octopus') {
callback(flavour);
}
}
describe('drinkAll', () => {
test('will drink something apple-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'apple');
expect(drink).toHaveBeenCalled();
});
test('does not drink something squid-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'squid');
expect(drink).not.toHaveBeenCalled();
});
});
.toHaveBeenCalledTimes(number)
This is also under the alias: .toBeCalledTimes(number)
You should use .toHaveBeenCalledTimes to ensure that a mock function got called exact number of times.
For instance, let us say you have a drinkEach(drink, Array<flavor>) function that will take a drink function and apply it to array of passed beverages. You might want to check that drink function was called exact number of times. You can do that with the test suite below:
test('drinkEach will drink each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});
toHaveBeenCalledWith(arg1, arg2, ...)
This is also under the alias: .toBeCalledWith()
You should use .toHaveBeenCalledWith to ensure that a mock function was called with specific arguments.
For instance, let us say that you can register a beverage with a register function, and applyToAll(f) has to apply the function f to all registered beverages. To make sure this will work, you might write:
test('registration will apply 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, ...)
This is also under the alias: .lastCalledWith(arg1, arg2, ...)
In the case where you have a mock function, you can use .toHaveBeenLastCalledWith to test what arguments it was last called with. For example, let's say you have an applyToAllFlavors(f) function, that applies f to a bunch of flavors, and you want to make sure that when you call it, the last flavor it will operate on is 'mango'. You use the code below:
test('applying does mango last to all flavors', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
This is also under the alias: .nthCalledWith(nthCall, arg1, arg2, ...)
In the case where you have a mock function, you can make use of .toHaveBeenNthCalledWith to test what arguments it was nth called with. For instance, let us say you have a drinkEach(drink, Array<flavor>) function which applies f to a bunch of flavors, and you want to make sure that when you call it, the first flavor it will operate on is 'apple' and the second one is 'squid'. You can write:
test('drinkEach will drink each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['apple', 'squid']);
expect(drink).toHaveBeenNthCalledWith(1, 'apple');
expect(drink).toHaveBeenNthCalledWith(2, 'squid');
});
Note: the nth argument has to be positive integer starting from 1.
.toHaveReturned()
This is also under the alias: .toReturn()
In the case where you have a mock function, you can make use of .toHaveReturned to test that the mock function successfully returned (i.e., did not throw an error) at least one time. For example, let's say you have a mock drink that returns true. You can write the code below:
test('drinks returns', () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned();
});
.toHaveReturnedTimes(number)
This is also under the alias: .toReturnTimes(number)
You should use .toHaveReturnedTimes to make sure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. Any calls to the mock function that throw an error will not be counted toward the number of times the function returned.
For instance, let us say you have a mock drink that returns true. You can write the following code:
test('drink will return twice', () => {
const drink = jest.fn(() => true);
drink();
drink();
expect(drink).toHaveReturnedTimes(2);
});
.toHaveReturnedWith(value)
This is also under the alias: .toReturnWith(value)
You should use .toHaveReturnedWith to ensure that a mock function returned a specific value.
For instance, let's say you have a mock drink that will return the name of the beverage that was consumed. You can write the following:
test('drink will return La Croix', () => {
const beverage = {name: 'La Croix'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage);
expect(drink).toHaveReturnedWith('La Croix');
});
.toHaveLastReturnedWith(value)
This is also under the alias: .lastReturnedWith(value)
You should use .toHaveLastReturnedWith to test the specific value that was last returned by mock function. In the case where the last call to the mock function threw an error, then this matcher fails no matter what value you provided as the expected return value.
For instance, given that you have a mock drink that returns the name of the beverage that was consumed. You can write the folllowing:
test('drink will return 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)
This is also under the alias: .nthReturnedWith(nthCall, value)
You should use .toHaveNthReturnedWith if you want to test the specific value that a mock function returned for the nth call. In the case where the nth call to the mock function threw an error, then this matcher fails no matter what value you provided as the expected return value.
For instance, let us say you have a mock drink that will return the name of the beverage that was consumed. You can write this:
test('drink will return 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)');
});
It should be noted that the nth argument must be positive integer starting from 1.
.toHaveLength(number)
You should use .toHaveLength to check that an object has a .length property and it is usually set to a certain numeric value.
This is very useful for checking arrays or strings size.
expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);
.toHaveProperty(keyPath, value?)
You should use .toHaveProperty to check if property at provided reference keyPath exists for an object. If you are checking deeply nested properties in an object you may use dot notation or an array containing the keyPath for deep references.
You can provide an optional value argument that compares the received property value (recursively for all properties of object instances, also known as deep equality, such as the toEqual matcher).
The following example will contain a houseForSale object with nested properties.
We use toHaveProperty to check for the existence and values of various properties in the object.
// Object that contains 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('my desired features are in this house', () => {
// Simple Referencing
expect(houseForSale).toHaveProperty('bath');
expect(houseForSale).toHaveProperty('bedrooms', 4);
expect(houseForSale).not.toHaveProperty('pool');
// Deep referencing using the 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 that contains 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 the dot in the key itself
expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall');
});
.toBeCloseTo(number, numDigits?)
To use exact equality with floating point numbers is a bad idea. Rounding means that intuitive things will fail. For instance, this test will fail:
test('adding works sanely with simple decimals', () => {
expect(0.2 + 0.1).toBe(0.3); // Fails!
});
It will fail because in JavaScript, 0.2 + 0.1 is actually 0.30000000000000004. Sorry.
Rather, you should use .toBeCloseTo. You should use numDigits to control how many digits after the decimal point to check. For instance, if you want to ensure that 0.2 + 0.1 is equal to 0.3 and has a precision of 5 decimal digits, you can use the test below:
test('adding will work sanely with simple decimals', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
The optional numDigits argument has default value 2, this means the criterion is Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2).
.toBeDefined()
You should use .toBeDefined to check that a variable is not undefined. For instance, if you just want to check that a function fetchNewFlavorIdea() will return something, you can write this:
test('a new flavor idea exists', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});
You can write expect(fetchNewFlavorIdea()).not.toBe(undefined), but it is better practice to avoid referring to undefined directly in your code.
.toBeFalsy()
You should use .toBeFalsy when you don't care what a value is, that is if you only want to ensure a value is false in a boolean context. For instance, let us say you have some application code which looks like:
drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}
You may not be bordered with what getErrors returns, specifically - it may return false, null, or 0, and your code would still work perfectly. So if you would like to test there are no errors after drinking some La Croix, you could write:
test('drinking La Croix does not lead to errors', () => {
drinkSomeLaCroix();
expect(getErrors()).toBeFalsy();
});
In JavaScript, we have six falsy values: false, 0, '', null, undefined, and NaN. Any other thing is truthy.
.toBeGreaterThan(number)
If you want to compare floating point numbers, you can utilize toBeGreaterThan. For instance, when you want to test that ouncesPerCan() returns a value of more than 20 ounces, write:
test('ounces per can is more than 20', () => {
expect(ouncesPerCan()).toBeGreaterThan(20);
});
.toBeGreaterThanOrEqual(number)
If you want to compare floating point numbers, use toBeGreaterThanOrEqual. For instance, if you need to test that ouncesPerCan() will return a value of at least 14 ounces, write:
test('ounces per can is at least 14', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(14);
});
.toBeLessThan(number)
If you want to compare floating point numbers, you can use toBeLessThan. For instance, if you want to test that ouncesPerCan() will return a value of less than 30 ounces, write:
test('ounces per can is less than 30', () => {
expect(ouncesPerCan()).toBeLessThan(30);
});
.toBeLessThanOrEqual(number)
If you want to compare floating point numbers, you can use toBeLessThanOrEqual. For example, if you want to test that ouncesPerCan() will return a value of at most 15 ounces, write:
test('ounces per can is at most 15', () => {
expect(`ouncesPerCan()).toBeLessThanOrEqual(15);
});
.toBeInstanceOf(Class)
You should use .toBeInstanceOf(Class) to check that an object is an instance of a class. This matcher will use instanceof underneath.
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws
.toBeNull()
.toBeNull() is the same as .toBe(null) but the error messages will be a bit nicer. So you should use .toBeNull() when you want to check that something is null.
function bloop() {
return null;
}
test('bloop will return null', () => {
expect(bloop()).toBeNull();
});
.toBeTruthy()
You should use .toBeTruthy when you don't care what a value is, you only want to ensure a value is true in a boolean context. For instance, let us say you have some application code that looks like:
drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}
You may not care what thirstInfo will return, specifically ? it can return true or a complex object, and your code will still work. So if you just need to test that thirstInfo will be truthy after drinking some La Croix, you might write:
test('drinking La Croix might lead to having thirst info', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});
There are six falsy values in JavaScript: false, 0, '', null, undefined, and NaN. Any other thing is truthy.
.toBeUndefined()
You should use .toBeUndefined to check that a variable is undefined. For instance, if you want to check that a function bestDrinkForFlavor(flavor) will return undefined for the 'squid' flavor, because there is no good squid-flavored drink:
test('the best drink for squid flavor is undefined', () => {
expect(bestDrinkForFlavor('squid')).toBeUndefined();
});
You could write expect(bestDrinkForFlavor('squid')).toBe(undefined), but it is a better practice to avoid referring to undefined directly in your code.
.toBeNaN()
You should use .toBeNaN when checking a value is NaN.
test('will pass when value is NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});
.toContain(item)
You should use .toContain if you want to check that an item is in an array. When testing the items in the array, this will use ===, a strict equality check. .toContain can equally check whether a string is a substring of another string.
For instance, if getAllFlavors() will return an array of flavors and you want to enure that lime is in there, you can write this:
test('the flavor list will contain lime', () => {
expect(getAllFlavors()).toContain('lime');
});
.toEqual(value)
You should use .toEqual to compare recursively all properties of object instances (also known as "deep" equality). It will call Object.is to compare primitive values, this is even better for testing than === strict equality operator.
For instance, .toEqual and .toBe behave differently in this test suite, so all the tests will pass:
const can1 = {
flavor: 'grapefruit',
ounces: 12,
};
const can2 = {
flavor: 'grapefruit',
ounces: 12,
};
describe('the La Croix cans that are on my desk', () => {
test('have all the same properties', () => {
expect(can1).toEqual(can2);
});
test('they are not the exact same can', () => {
expect(can1).not.toBe(can2);
});
});
Note: .toEqual will not perform a deep equality check for two errors. Only the message property of an Error will be considered for equality. It is recommended that you use the .toThrow matcher for testing against errors.
If the differences between properties do not help you to understand why a test fails, especially if the report is large, then you can move the comparison into the expect function. For instance, you should use equals method of Buffer class to assert whether or not buffers has the same content:
- You should rewrite expect(received).toEqual(expected) as expect(received.equals(expected)).toBe(true)
- you should rewrite expect(received).not.toEqual(expected) as expect(received.equals(expected)).toBe(false)
.toMatch(regexpOrString)
You should use .toMatch to check that a string matches a regular expression.
For instance, you may not know what exactly essayOnTheBestFlavor() will return, but you know it is a really long string, and the substring orangefruit should be in there somewhere. This can be tested with:
describe('this is an essay on the best flavor', () => {
test('mentions orangefruit', () => {
expect(essayOnTheBestFlavor()).toMatch(/orangefruit/);
expect(essayOnTheBestFlavor()).toMatch(new RegExp('orangefruit'));
});
});
This matcher will also accept a string, which it tries to match:
describe('orangefruits are healthy', () => {
test('orangefruits are a fruit', () => {
expect('orangefruits').toMatch('fruit');
});
});
.toMatchObject(object)
You have to use .toMatchObject to check whether a JavaScript object matches a subset of the properties of an object. It will match received objects with properties which are not in the expected object.
You can equally pass an array of objects, in which case the method returns true only if each object in the received array matches (in the toMatchObject sense described above) the corresponding object in the expected array. This is useful if you need to check whether two arrays match in their number of elements, as opposed to arrayContaining, which will allow for extra elements in the received array.
You can match properties against matchers or against values.
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('this house has my desired features', () => {
expect(houseForSale).toMatchObject(desiredHouse);
});```
```describe('toMatchObject that is applied to arrays', () => {
test('the number of elements has to match exactly', () => {
expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]);
});
test('.toMatchObject will be called for each elements, hence extra object properties are okay', () => {
expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([
{foo: 'bar'},
{baz: 1},
]);
});
});
.toMatchSnapshot(propertyMatchers?, hint?)
This will ensure that a value matches the most recent snapshot. An optional propertyMatchers object argument can be provided, which will have asymmetric matchers as values of a subset of expected properties, if the received value is an object instance. It is similar to toMatchObject with flexible criteria for a subset of properties, followed by a snapshot test as the exact criteria for the rest of the properties.
An optional hint string argument that is appended to the test name can be provided. Although Jest will always append a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate between multiple snapshots in a single it or test block. Jest will sort snapshots by name in the corresponding .snap file.
.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)
This will ensure that a value matches the most recent snapshot.
An optional propertyMatchers object argument which has asymmetric matchers as values of a subset of expected properties can be provided, if the received value is an object instance. It is similar toMatchObject with flexible criteria for a subset of properties, and then followed by a snapshot test as exact the criteria for the rest of the properties.
Jest will add the inlineSnapshot string argument to the matcher in the test file (rather than an external .snap file) the first time that the test runs.
.toStrictEqual(value)
You should use .toStrictEqual to test that objects have the same types as well as structure.
Differences from .toEqual:
- Keys that have undefined properties are checked. e.g. {a: undefined, b: 2} does not match {b: 2} when using .toStrictEqual.
- Array sparseness will be checked. e.g. [, 1] does not match [undefined, 1] when using .toStrictEqual.
- Object types will be checked to be equal. e.g. A class instance with fields a and b will not equal a literal object with fields a and b.
class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}
describe('the La Croix cans on my desk', () => {
test('they are not semantically the same', () => {
expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'});
expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'});
});
});
.toContainEqual(item)
You will need to use .toContainEqual when you want to check that an item with a specific structure and values will be contained in an array. For testing the items in the array, this matcher will recursively check the equality of all fields, instead of checking for object identity.
describe('my beverage', () => {
test('is sour and not delicious', () => {
const myBeverage = {delicious: false, sour: true};
expect(myBeverages()).toContainEqual(myBeverage);
});
});
.toThrow(error?)
This is also under the alias: .toThrowError(error?)
You should use .toThrow to test that a function throws when it is called. For instance, if we want to test that drinkFlavor('squid') throws, because squid flavor is too disgusting to drink, we could write:
test('throws on squid', () => {
expect(() => {
drinkFlavor('squid');
}).toThrow();
});
An optional argument to test that a specific error is thrown can be provided:
- regular expression: error message matches the pattern
- string: error message includes the substring
- error object: error message is equal to the message property of the object
- error class: error object is instance of class
For example, let's say that drinkFlavor is coded like this:
function drinkFlavor(flavor) {
if (flavor == 'squid') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other things
}
We could test this error gets thrown in several ways:
test('throws on squid', () => {
function drinkSquid() {
drinkFlavor('squid');
}
// Test that the error message will say "yuck" somewhere: these are equivalent
expect(drinkOctopus).toThrowError(/yuck/);
expect(drinkOctopus).toThrowError('yuck');
// Test the exact error message
expect(drinkOctopus).toThrowError(/^yuck, squid flavor$/);
expect(drinkOctopus).toThrowError(new Error('yuck, squidh flavor'));
// Test that we get a DisgustingFlavorError
expect(drinkOctopus).toThrowError(DisgustingFlavorError);
});
.toThrowErrorMatchingSnapshot(hint?)
You should use .toThrowErrorMatchingSnapshot to test that a function throws an error matching the most recent snapshot when it is called.
An optional hint string argument that is appended to the test name can be provided. Although Jest will always append a number at the end of a snapshot name, short descriptive hints may be more useful than numbers in differentiating multiple snapshots in a single it or test block. Jest will sort snapshots by name in the corresponding .snap file.
For instance, let us say you have a drinkFlavor function that throws whenever the flavor is 'squid', and will be coded like this:
function drinkFlavor(flavor) {
if (flavor == 'squid') {
throw new DisgustingFlavorError('yuck, squid flavor');
}
// Do some other thing
}
The test for this function looks this way:
test('throws on squid', () => {
function drinkSquid() {
drinkFlavor('squid');
}
expect(drinkSquid).toThrowErrorMatchingSnapshot();
});
This will generate the following snapshot:
exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`;
.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
You use .toThrowErrorMatchingInlineSnapshot to test that a function will throw an error matching the most recent snapshot when it is called.
Jest will add the inlineSnapshot string argument to the matcher in the test file (rather than an external .snap file) the first time that the test runs.
Previous:
Testing Web Frameworks with Jest: Vue, Redux, and Express.
Next:
Jest Global Methods: beforeEach, afterEach, beforeAll, afterAll.
It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.
https://www.w3resource.com/jest/expect.php
- Weekly Trends and Language Statistics
- Weekly Trends and Language Statistics