Сравнение значений: toBe() и toEqual()

Сравнение значений в Protractor опирается на матчеры фреймворка Jasmine. Ключевыми для повседневных проверок являются toBe() и toEqual(). Несмотря на внешнюю схожесть, они решают принципиально разные задачи и по-разному интерпретируют «равенство».

toBe() использует оператор строгого равенства ===. Проверка проходит только в том случае, если сравниваемые значения идентичны на уровне JavaScript-движка.

Примитивные типы

Для примитивов (number, string, boolean, null, undefined) результат обычно интуитивно понятен:

expect(5).toBe(5);
expect('login').toBe('login');
expect(true).toBe(true);

Любое различие в типе или значении приводит к падению теста:

expect(5).toBe('5'); // false

Объекты и массивы

Для ссылочных типов toBe() сравнивает ссылки, а не содержимое:

const a = { role: 'admin' };
const b = { role: 'admin' };

expect(a).toBe(b); // false

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

Единственный случай, когда сравнение проходит:

const user = { id: 1 };

expect(user).toBe(user); // true

Особые случаи

  • NaN никогда не равен самому себе:

    expect(NaN).toBe(NaN); // false
  • Функции сравниваются по ссылке:

    const fn = () => {};
    expect(fn).toBe(fn); // true

toEqual() — глубокое структурное сравнение

toEqual() выполняет рекурсивное сравнение значений. Для сложных структур анализируется содержимое, а не ссылка.

Объекты

const actual = { name: 'Alice', age: 30 };
const expected = { name: 'Alice', age: 30 };

expect(actual).toEqual(expected); // true

Порядок свойств не имеет значения:

expect(
  { a: 1, b: 2 }
).toEqual(
  { b: 2, a: 1 }
);

Массивы

Массивы сравниваются поэлементно и с учётом порядка:

expect([1, 2, 3]).toEqual([1, 2, 3]); // true
expect([1, 2, 3]).toEqual([3, 2, 1]); // false

Вложенные структуры

toEqual() корректно обрабатывает вложенные объекты и массивы:

expect({
  user: {
    roles: ['admin', 'editor']
  }
}).toEqual({
  user: {
    roles: ['admin', 'editor']
  }
});

Специальные типы

  • Date сравниваются по значению времени:

    expect(new Date(2025, 0, 1)).toEqual(new Date(2025, 0, 1));
  • Регулярные выражения сравниваются по шаблону и флагам.

  • NaN считается равным NaN:

    expect(NaN).toEqual(NaN); // true

Контекст Protractor: асинхронные значения

В Protractor часто сравниваются значения, полученные из браузера: текст элементов, атрибуты, результаты вычислений в Angular-приложении.

const title = element(by.css('h1')).getText();
expect(title).toBe('Dashboard');

При использовании async/await:

const title = await element(by.css('h1')).getText();
expect(title).toEqual('Dashboard');

Для строк и чисел разницы между toBe() и toEqual() здесь не будет, но для возвращаемых структур (например, JSON из browser.executeScript) выбор матчера становится критичным.

Типичные ошибки при выборе матчера

Ошибка: использование toBe() для объектов

expect(response).toBe({
  status: 'ok'
});

Тест всегда будет падать, даже если объект идентичен по содержимому.

Корректный вариант:

expect(response).toEqual({
  status: 'ok'
});

Ошибка: избыточное применение toEqual() для примитивов

expect(isLoggedIn).toEqual(true);

Работает корректно, но логически менее точно отражает намерение. Для строгих логических проверок предпочтительнее toBe().

Семантика и читаемость тестов

Выбор между toBe() и toEqual() влияет не только на результат, но и на смысл теста:

  • toBe() подчёркивает идентичность и строгость
  • toEqual() подчёркивает соответствие структуры и данных

В хорошо написанных тестах матчер отражает ожидаемую природу сравнения, а не просто «чтобы тест прошёл».

Краткое сопоставление

Критерий toBe() toEqual()
Основа сравнения === Глубокое сравнение
Примитивы Да Да
Объекты По ссылке По структуре
Массивы По ссылке Поэлементно
NaN Не равен Равен
Назначение Строгая идентичность Эквивалентность данных

Грамотное различение этих матчеров — обязательная основа надёжных e2e-тестов в Protractor и Jasmine.