Равенство: toBe против toEqual

Урок объясняет ключевую разницу между матчерами toBe и toEqual.

toBe сравнивает по ссылке/значению (как ===), а toEqual сравнивает структуру объектов рекурсивно.

toBe — строгое равенство

toBe использует Object.is, что для примитивов почти совпадает с ===. Идеален для чисел, строк, булевых значений:

expect(2 + 2).toBe(4);
expect('abc').toBe('abc');
expect(true).toBe(true);

Но для объектов и массивов toBe проверяет, что это один и тот же объект в памяти. Два разных объекта с одинаковым содержимым по toBe не равны.

Почему toBe не годится для объектов

В JavaScript два литерала {a: 1} — это два разных объекта. Проверим это на живом примере:

const a = { x: 1 };
const b = { x: 1 };
const c = a;

console.log('a === b:', a === b); // разные объекты
console.log('a === c:', a === c); // одна ссылка
console.log('содержимое одинаковое:',
  JSON.stringify(a) === JSON.stringify(b));

Вывод:

a === b: false
a === c: true
содержимое одинаковое: true

Видно: содержимое одинаковое, но === (а значит и toBe) считает a и b разными. Поэтому для сравнения структур нужен другой матчер.

toEqual — структурное сравнение

toEqual рекурсивно обходит объект и сравнивает значения всех полей. Содержимое совпадает — тест зелёный, даже если это разные объекты:

expect({ name: 'Аня', age: 30 }).toEqual({ name: 'Аня', age: 30 }); // OK
expect([1, 2, 3]).toEqual([1, 2, 3]); // OK

expect({ name: 'Аня' }).toBe({ name: 'Аня' }); // провал: разные ссылки

Тонкость с undefined: toStrictEqual

toEqual игнорирует поля со значением undefined. Если важна и эта разница, используйте toStrictEqual — он также проверяет тип (например, отличает обычный объект от экземпляра класса):

expect({ a: 1, b: undefined }).toEqual({ a: 1 });        // проходит
expect({ a: 1, b: undefined }).toStrictEqual({ a: 1 });  // НЕ проходит

Памятка

МатчерКогда использовать
toBeпримитивы (числа, строки, булевы) или проверка той же ссылки
toEqualобъекты и массивы по содержимому
toStrictEqualстрогое сравнение, чувствительное к undefined и типу

Итог

  • toBe — для примитивов и сравнения ссылок.
  • toEqual — для объектов/массивов по содержимому.
  • Два разных объекта с одинаковыми полями равны по toEqual, но не по toBe.
  • toStrictEqual учитывает undefined-поля и тип.
Проверьте себя
1. Какой матчер выбрать для сравнения двух объектов по их содержимому?
AtoBe
BtoEqual
CtoContain
DtoMatch
2. Почему expect({a:1}).toBe({a:1}) провалится?
AПотому что toBe не работает с числами
BПотому что это два разных объекта в памяти, а toBe сравнивает ссылки
CПотому что нужно писать toEqualStrict
DПотому что объекты нельзя тестировать
3. Чем toStrictEqual отличается от toEqual?
AОн быстрее
BОн учитывает поля со значением undefined и тип объекта
CОн работает только с массивами
DОн игнорирует вложенные объекты
Поддержать проект