Тестирование 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
для проверки на равенство.