Тестирование JavaScript Jasmine - toBe vs toEqual

Скажем, у меня есть следующее:

var myNumber = 5;
expect(myNumber).toBe(5);
expect(myNumber).toEqual(5);

Оба вышеуказанных теста пройдут. Есть ли разница между toBe() и toEqual(), когда дело доходит до оценки чисел? Если это так, когда я должен использовать один, а не другой?

Ответ 1

Для примитивных типов (например, числа, логические значения, строки и т.д.) Нет разницы между toBe и toEqual; один из них будет работать для 5, true или "the cake is a lie".

Чтобы понять разницу между toBe и toEqual, давайте представим три объекта.

var a = { bar: 'baz' },
    b = { foo: a },
    c = { foo: a };

При строгом сравнении (===) некоторые вещи "одинаковы":

> b.foo.bar === c.foo.bar
true

> b.foo.bar === a.bar
true

> c.foo === b.foo
true

Но некоторые вещи, даже если они "равны", не являются "одинаковыми", поскольку они представляют объекты, которые живут в разных местах памяти.

> b === c
false

Jasmine toBe matcher - не более чем оболочка для строгого сравнения на равенство

expect(c.foo).toBe(b.foo)

это то же самое, что и

expect(c.foo === b.foo).toBe(true)

Не поверь мне на слово; см. исходный код toBe.

Но b и c представляют функционально эквивалентные объекты; они оба выглядят как

{ foo: { bar: 'baz' } }

Не было бы замечательно, если бы мы могли сказать, что b и c "равны", даже если они не представляют один и тот же объект?

Введите toEqual, который проверяет "глубокое равенство" (т.е. выполняет рекурсивный поиск по объектам, чтобы определить, эквивалентны ли значения их ключей). Оба следующих теста пройдут:

expect(b).not.toBe(c);
expect(b).toEqual(c);

Надеюсь, это поможет прояснить некоторые вещи.

Ответ 2

toBe() по сравнению с toEqual(): toEqual() проверяет эквивалентность. toBe(), с другой стороны, гарантирует, что они являются одним и тем же объектом.

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

При сравнении примитивных типов toEqual() и toBe() даст тот же результат. При сравнении объектов toBe() является более строгим сравнением, и если это не тот же самый объект в памяти, это вернет false. Поэтому, если вы не хотите убедиться, что это тот же самый объект в памяти, используйте toEqual() для сравнения объектов.

Посмотрите эту ссылку для получения дополнительной информации: http://evanhahn.com/how-do-i-jasmine/

Теперь, когда вы смотрите на разницу между toBe() и toEqual(), когда дело доходит до чисел, не должно быть никаких различий, пока ваше сравнение будет правильным. 5 всегда будет эквивалентно 5.

Хорошее место для игры с этим, чтобы увидеть разные результаты, здесь

Обновление

Легкий способ взглянуть на toBe() и toEqual() - понять, что именно они делают в JavaScript. В соответствии с API Jasmine нашла здесь:

toEqual() работает для простых литералов и переменных и должен работать для объектов

toBe() сравнивается с ===

По сути, это то, что toEqual() и toBe() похожи на Javascripts === оператор, кроме toBe() также проверяет, является ли он тем же самым объектом, что и в примере ниже objectOne === objectTwo //returns false as Что ж. Однако toEqual() вернет true в этой ситуации.

Теперь вы можете хотя бы понять, почему, когда дано:

var objectOne = {
    propertyOne: str,
    propertyTwo: num    
}

var objectTwo = {
    propertyOne: str,
    propertyTwo: num    
}

expect(objectOne).toBe(objectTwo); //returns false

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

Ответ 3

Чтобы процитировать проект jasmine github,

expect(x).toEqual(y); сравнивает объекты или примитивы x и y и проходит, если они эквивалентны

expect(x).toBe(y); сравнивает объекты или примитивы x и y и проходит , если они являются одним и тем же объектом

Ответ 4

Глядя на исходный код Жасмина, он проливает больше света на проблему.

toBe очень просто и просто использует оператор тождества/строгого равенства, ===:

  function(actual, expected) {
    return {
      pass: actual === expected
    };
  }

toEqual, с другой стороны, имеет длину около 150 строк и имеет специальную обработку для встроенных объектов, таких как String, Number, Boolean, Date, Error, Element и RegExp. Для других объектов он рекурсивно сравнивает свойства.

Это сильно отличается от поведения оператора равенства ==. Например:

var simpleObject = {foo: 'bar'};
expect(simpleObject).toEqual({foo: 'bar'}); //true
simpleObject == {foo: 'bar'}; //false

var castableObject = {toString: function(){return 'bar'}};
expect(castableObject).toEqual('bar'); //false
castableObject == 'bar'; //true

Ответ 5

Думаю, кому-то может понравиться объяснение (аннотированный) пример:

Ниже, если моя функция deepClone() выполняет свою работу правильно, тест (как описано в вызове 'it()') будет успешным:

describe('deepClone() array copy', ()=>{
    let source:any = {}
    let clone:any = source
    beforeAll(()=>{
        source.a = [1,'string literal',{x:10, obj:{y:4}}]
        clone = Utils.deepClone(source) // THE CLONING ACT TO BE TESTED - lets see it it does it right.
    })
    it('should create a clone which has unique identity, but equal values as the source object',()=>{
        expect(source !== clone).toBe(true) // If we have different object instances...
        expect(source).not.toBe(clone) // <= synonymous to the above. Will fail if: you remove the '.not', and if: the two being compared are indeed different objects.
        expect(source).toEqual(clone) // ...that hold same values, all tests will succeed.
    })
})

Конечно, это не полный набор тестов для моего deepClone(), так как я здесь не проверял, имеют ли литерал объекта в массиве (и вложенный в него) также различную идентичность, но одинаковые значения.

Ответ 6

toEqual() сравнивает значения, если Primitive или содержимое, если Objects. toBe() сравнивает ссылки.

Следующий код/​​пакет должен быть понятен:

describe('Understanding toBe vs toEqual', () => {
  let obj1, obj2, obj3;

  beforeEach(() => {
    obj1 = {
      a: 1,
      b: 'some string',
      c: true
    };

    obj2 = {
      a: 1,
      b: 'some string',
      c: true
    };

    obj3 = obj1;
  });

  afterEach(() => {
    obj1 = null;
    obj2 = null;
    obj3 = null;
  });

  it('Obj1 === Obj2', () => {
    expect(obj1).toEqual(obj2);
  });

  it('Obj1 === Obj3', () => {
    expect(obj1).toEqual(obj3);
  });

  it('Obj1 !=> Obj2', () => {
    expect(obj1).not.toBe(obj2);
  });

  it('Obj1 ==> Obj3', () => {
    expect(obj1).toBe(obj3);
  });
});

Ответ 7

Обратите внимание:

  • toBe() рассматривает сравнения как Object.is().
  • toEqual() рассматривает сравнения так же, как ===.

Вот почему для примитивных типов toBe и toEqual не имеют большой разницы при проверке на равенство, но для ссылочных типов, таких как объекты, вы бы предпочли использовать toEqual для проверки на равенство.