QuestionsTesting

Assertions and matchers: toBe vs. toEqual

AssertionsEasyTesting

What's the difference between toBe and toEqual? When do you reach for each, and what other matchers matter?

What it tests

Understanding reference vs. structural equality in assertions — a classic source of confusing failures.

Approach & answer

toBe checks REFERENCE / primitive identity — it's Object.is, essentially ===. Use it for primitives (numbers, strings, booleans) and when you specifically want to assert two variables point to the SAME object instance. toEqual checks STRUCTURAL / deep equality — it recursively compares the contents of objects and arrays, so two different objects with the same shape and values pass. The classic bug: expect({a:1}).toBe({a:1}) FAILS because they're two distinct objects with different references, even though their contents match; you wanted toEqual. Conversely, using toEqual where you meant to assert identity can hide a bug where a function returned a fresh copy instead of the same reference. A few related nuances: toEqual ignores undefined properties and array holes; toStrictEqual is stricter (checks undefined props and that types/classes match), useful when the exact shape matters. Beyond equality, the matchers worth knowing keep tests readable and failures diagnostic: toContain (array/string membership), toMatch (regex on strings), toThrow (a function throws, optionally matching a message), toHaveBeenCalledWith (a mock/spy was called with given args), toBeCloseTo (floating-point comparison, since 0.1+0.2 !== 0.3), and truthiness helpers toBeTruthy/toBeNull/toBeDefined. For DOM there are jest-dom matchers like toBeVisible/toHaveTextContent that produce far better failure messages than poking at properties. Choosing the RIGHT matcher matters beyond correctness: a precise matcher gives a precise failure message ('expected 3 to be 4', not 'expected true to be false'), which is half the value of a test.

Use this technique when

Choosing the correct assertion; debugging a 'they look equal but toBe fails' surprise.

Code

expect(2 + 2).toBe(4);              // primitives: reference/=== is fine

expect({ a: 1 }).toBe({ a: 1 });   // FAILS: different object references
expect({ a: 1 }).toEqual({ a: 1 });// passes: deep structural equality

expect([1, 2, 3]).toContain(2);
expect(() => parse('')).toThrow(/empty/);
expect(0.1 + 0.2).toBeCloseTo(0.3); // floats: never toBe(0.3)
expect(mockFn).toHaveBeenCalledWith('id-42');

References