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

349

Допустим, у меня есть следующее:

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

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

Ллойд Бэнкс
источник
в двух словах: нет никакой разницы между ними при сравнении примитивов; для объектов -> toEqual()будет сравнивать по ключу / значениям-контенту; toBe()будет сравнивать по ссылке на объект.
Андре Элрико

Ответы:

489

Для примитивных типов (например, числа, логические значения, строки и т. Д.) Нет разницы между 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

toBeМэтчер Жасмин - не более чем обертка для строгого сравнения на равенство

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

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

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

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

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

{ foo: { bar: 'baz' } }

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

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

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

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

elreimundo
источник
17
«Для примитивных типов (например, чисел, логических значений, строк и т. Д.) Нет разницы между toBe и toEqual» - оказывается, это не совсем так. expect(0).toBe(-0)пройдет, но не expect(0).toEqual(-0)получится.
mgol
11
tl; dr - toBeиспользует строгое равенство - сравнивает по ссылке, toEqualиспользует свойство эквивалентности. Рекомендуется использовать toEqualдля примитивов
Drenai
1
Так что же нам использовать для примитивов и почему? Дренаи, почему вы рекомендуете Equal?
Патрик Салапски
@PatrickSzalapski я могу только догадываться рассуждения Denai, но toEqualгораздо более осторожными относительно равенства ( 0 != -0, "hi" = new String("hi")и т.д.), так что я бы рекомендовал использовать toEqual только , если вы на самом деле обеспокоены эталонной эквивалентности. Просмотреть все проверки toEqualв eqметоде можно здесь: github.com/jasmine/jasmine/blob/master/src/core/matchers/….
Река
Я думаю, что лучше использовать toBe при сравнении примитивов, чтобы сохранить накладные расходы, которые выполняются в toEqual.
ГарфилдКлон
81

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. Согласно Jasmine API, найдены здесь :

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

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

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

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

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

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

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

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

Adjit
источник
4
Это позволяет избежать ответа на вопрос. Вы объясняете, что toEqual()значит, toEqual()проверяя эквивалентность , но следующий очевидный вопрос - хорошо, так что же означает «эквивалент»? Описание алгоритма, использованного для определения «эквивалентности», или, по крайней мере, примеры случаев, когда поведение toEqual()и toBe()отличается, сделало бы это более полезным.
Марк Эмери
8
Это не только не отвечает на вопрос, но это неправильно . toEqualследует использовать для глубокого сравнения объектов, а не toBe. jsfiddle.net/bBL9P/67
Ллойд Бэнкс
3
Кажется, что люди не удосуживаются проверить, правильно ли то, что они говорят. И toBe, и toEqual кажутся строгими сравнениями. Проверьте это ... Так что в моем тестировании я еще не нашел разницу. например: var f = 1; var g = "1" ожидают (f == g) .toEqual (верно); // истинно ожидают (f) .toEqual (g); // ложно ожидают (f) .toBe (g); // ложно
user1809104
6
Это совершенно неправильно. toEqualэто совсем не то же самое, что ==.
Meagar
6
Прочитайте комментарии выше. expect(1).toEqual('1')не получается, пока 1 == '1'верно. toEqualне имеет ничего общего с ==. Это как ===за исключением того, что он будет сравнивать объекты способом, аналогичным сравнению по значению.
Meagar
33

Процитирую проект Jasmine Github,

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

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

Тарака
источник
14

Взгляд на исходный код Jasmine проливает больше света на эту проблему.

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
Тамлин
источник
2

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);
  });
});
BeingSuman
источник
1

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

Ниже, если моя функция 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 (), поскольку я здесь не проверял, имеют ли литерал объекта в массиве (и вложенный в него) также различную идентичность, но одинаковые значения.

Джаред Томашевский
источник
0

Я думаю, что toEqual проверяет глубокое равенство, toBe является той же ссылкой 2 переменной

  it('test me', () => {
    expect([] === []).toEqual(false) // true
    expect([] == []).toEqual(false) // true

    expect([]).toEqual([]); // true // deep check
    expect([]).toBe([]); // false
  })
фейзулла йылдыз
источник
-2

Примечания к сведению:

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

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

Джон Мутума
источник