Understanding Jest's Core Matchers
A matcher is the method you chain onto expect() to describe what a value should look like. Jest ships dozens of matchers, but toBe, toEqual, and toContain cover the vast majority of everyday assertions: comparing primitives, comparing whole objects or arrays, and checking membership inside a collection. Picking the right matcher matters because each one compares values differently, and using the wrong one can make a test pass when it should fail, or fail when the code is actually correct.
Cricket analogy: Choosing toBe versus toEqual is like a scorer deciding whether to record the exact ball-by-ball sequence or just the final scorecard total; both describe the innings, but only one captures the precise detail you actually need.
toBe: Strict Equality with Object.is
toBe checks strict equality using Object.is, which behaves like the === operator with two small differences: NaN is equal to NaN, and +0 is not equal to -0. This makes toBe perfect for primitives such as numbers, strings, booleans, null, and undefined. For objects and arrays, toBe checks reference identity, not content, so two separately created objects with identical properties will fail a toBe comparison even though they look the same when logged to the console.
Cricket analogy: toBe is like verifying it's literally the same match ball used since the coin toss, not just a ball of the same brand and condition; two 'identical-looking' Kookaburra balls are still different physical objects.
test('primitive values use toBe', () => {
expect(2 + 2).toBe(4);
expect('hello'.toUpperCase()).toBe('HELLO');
expect(null).toBe(null);
});
test('toBe fails for structurally identical objects', () => {
const a = { id: 1 };
const b = { id: 1 };
expect(a).not.toBe(b); // different references
expect(a).toBe(a); // same reference
});toEqual: Deep Equality for Objects and Arrays
toEqual recursively compares every enumerable property of two objects or every element of two arrays, ignoring reference identity entirely. Two separately constructed objects with the same keys and values will pass toEqual even though toBe would fail on them. toEqual ignores undefined properties and array items by default, which is convenient for comparing API responses but can occasionally hide a bug where a field is missing rather than merely undefined; toStrictEqual exists specifically to catch that distinction, along with differences in object type (for example a class instance versus a plain object) and sparse array holes.
Cricket analogy: toEqual is like comparing two scorecards from different scorers who watched the same match; as long as every run, wicket, and over tally matches, it doesn't matter whose physical notebook it is.
Use toStrictEqual instead of toEqual when you need to also catch undefined properties, sparse array holes, and differences between object types like class instances versus plain objects.
toContain: Checking Array and Iterable Membership
toContain checks whether a specific primitive value exists inside an array, string, Set, or other iterable, using === equality under the hood. It is the right choice when you only care that an item is present, not the entire collection's contents or order. When the array holds objects rather than primitives, toContain will not find a structural match because it relies on reference equality; toContainEqual solves this by using the same deep-equality algorithm as toEqual to find a matching element anywhere in the array.
Cricket analogy: toContain is like checking whether a specific player's name appears anywhere on the scorecard, without caring what order the batting lineup was in or what everyone else scored.
test('toContain checks membership', () => {
const fruits = ['apple', 'banana', 'mango'];
expect(fruits).toContain('banana');
expect('team player').toContain('team');
});
test('toContainEqual finds a structural match', () => {
const users = [{ id: 1, name: 'Ada' }, { id: 2, name: 'Grace' }];
expect(users).toContainEqual({ id: 2, name: 'Grace' });
// toContain would fail here because it compares by reference
});A frequent mistake is using toBe to compare two objects or arrays that were built separately, such as the result of JSON.parse(JSON.stringify(x)). Even with identical content, toBe will fail because the references differ; use toEqual instead.
- toBe uses Object.is (like === with NaN/±0 fixes) and is best for primitives.
- toBe compares objects and arrays by reference, not by content.
- toEqual performs a recursive, deep comparison of object and array content.
- toStrictEqual additionally catches undefined properties, sparse holes, and object type mismatches.
- toContain checks for a primitive value's presence in an array, string, or Set.
- toContainEqual finds an object or array inside a collection using deep equality.
- Choosing the wrong matcher can silently mask bugs, so match the matcher to the data shape.
Practice what you learned
1. Why does expect({id: 1}).toBe({id: 1}) fail even though both objects look identical?
2. Which matcher would you use to assert that two arrays of numbers contain the same values in the same order, as separate array instances?
3. What does toStrictEqual catch that toEqual does not?
4. Given const arr = [{id: 1}, {id: 2}], which assertion correctly checks that an object with id 2 exists in the array?
5. Which matcher would correctly compare 2 + 2 to 4?
Was this page helpful?
You May Also Like
Truthiness and Number Matchers
Master Jest's matchers for null, undefined, and boolean-like values, plus numeric comparisons including safe handling of floating-point precision.
Testing Exceptions with toThrow
Learn how to assert that a function throws an error using toThrow, including matching messages, error types, and async rejections.
Snapshot Testing
Understand how Jest's snapshot testing captures serialized output over time, when it's a good fit, and how to manage snapshot files responsibly.