Jasmine JavaScript Testing - toBe vs toEqual

349

Powiedzmy, że mam następujące elementy:

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

Oba powyższe testy przejdą pomyślnie. Czy istnieje różnica pomiędzy toBe()i toEqual()jeśli chodzi o ocenę liczb? Jeśli tak, to kiedy powinienem użyć jednego, a nie drugiego?

Lloyd Banks
źródło
w skrócie: bez różnicy między nimi przy porównywaniu prymitywów; dla obiektów -> toEqual()porówna według klucza / wartości-zawartości; toBe()porówna według odwołania do obiektu.
Andre Elrico,

Odpowiedzi:

489

W przypadku typów pierwotnych (np. Liczb, boolanów, ciągów itp.) Nie ma różnicy między toBei toEqual; jeden z nich będzie pracować 5, truealbo "the cake is a lie".

Aby zrozumieć różnicę między toBei toEqual, wyobraźmy sobie trzy obiekty.

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

Używając ścisłego porównania ( ===), niektóre rzeczy są „takie same”:

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

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

> c.foo === b.foo
true

Ale niektóre rzeczy, nawet jeśli są „równe”, nie są „takie same”, ponieważ reprezentują obiekty, które żyją w różnych miejscach w pamięci.

> b === c
false

toBeDobieracz Jasmine to nic innego jak opakowanie do ścisłego porównania równości

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

to to samo co

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

Nie wierz mi na słowo; zobacz kod źródłowy toBe .

Ale bi creprezentują funkcjonalnie równoważne obiekty; oboje wyglądają

{ foo: { bar: 'baz' } }

Czy nie byłoby wspaniale, gdybyśmy mogli to powiedzieć bi csą „równi”, nawet jeśli nie reprezentują tego samego obiektu?

Enter toEqual, który sprawdza „głęboką równość” (tzn. Dokonuje rekurencyjnego przeszukiwania obiektów w celu ustalenia, czy wartości ich kluczy są równoważne). Oba następujące testy zostaną zaliczone:

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

Mam nadzieję, że to pomoże wyjaśnić niektóre rzeczy.

elreimundo
źródło
17
„W przypadku typów pierwotnych (np. Liczb, boolanów, ciągów itp.) Nie ma różnicy między toBe a ToEqual” - jak się okazuje, nie jest to do końca prawda. expect(0).toBe(-0)przejdzie, ale expect(0).toEqual(-0)zawiedzie.
mgol
11
tl; dr - toBeużywa ścisłej równości - porównuje przez odniesienie, toEqualużywa równoważności właściwości. Zalecane do stosowania w toEqualprzypadku prymitywów
Drenai
1
Którego powinniśmy użyć do prymitywów i dlaczego? Drenai, dlaczego polecasz Equal?
Patrick Szalapski,
@PatrickSzalapski Mogę się tylko domyślać rozumowania Denai, ale toEqualjest o wiele bardziej ostrożny o równości ( 0 != -0, "hi" = new String("hi")itp), więc polecam korzystania toEqual wyłącznie chyba że faktycznie zaniepokojony równoważności odniesienia. Zobacz wszystkie kontrole dokonanetoEqual w eqmetodzie tutaj: github.com/jasmine/jasmine/blob/master/src/core/matchers/…
River
Myślę, że lepiej jest używać toBe podczas porównywania operacji pierwotnych, aby zaoszczędzić na nakładzie pracy wykonywanym przez toEqual.
GarfieldKlon
81

toBe()versus toEqual(): toEqual()sprawdza równoważność. toBe()z drugiej strony upewnia się, że są dokładnie tym samym obiektem.

Powiedziałbym „użyj” toBe()przy porównywaniu wartości i toEqual()przy porównywaniu obiektów.

Porównując prymitywne typy toEqual()i toBe()da ten sam wynik. Podczas porównywania obiektów toBe()jest bardziej rygorystyczne porównanie, a jeśli nie jest to dokładnie ten sam obiekt w pamięci, zwróci wartość false. Więc jeśli nie chcesz się upewnić, że jest to dokładnie ten sam obiekt w pamięci, użyj go toEqual()do porównania obiektów.

Sprawdź ten link, aby uzyskać więcej informacji: http://evanhahn.com/how-do-i-jasmine/

Teraz, patrząc na różnicę między toBe()i toEqual(), jeśli chodzi o liczby, nie powinno być żadnej różnicy tak długo, jak to porównanie jest poprawne. 5zawsze będzie równoważne z 5.

Miłe miejsce do zabawy i zobaczenia różnych wyników jest tutaj

Aktualizacja

Łatwym sposobem na sprawdzenie toBe()i toEqual()zrozumienie, co dokładnie robią w JavaScript. Według Jasmine API, znaleziono tutaj :

Funkcja toEqual () działa dla prostych literałów i zmiennych i powinna działać dla obiektów

toBe () porównuje z ===

Zasadniczo to, co to mówi, jest toEqual()i toBe()jest podobnym ===operatorem Javascripts , oprócz tego toBe()sprawdza również, czy jest to dokładnie ten sam obiekt, w tym również w przykładzie poniżej objectOne === objectTwo //returns false. Jednak toEqual()w tej sytuacji zwróci wartość true.

Teraz możesz przynajmniej zrozumieć, dlaczego:

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

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

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

To dlatego, że, jak wspomniano w tym odpowiedzi na inną, ale podobną pytanie,=== operator faktycznie oznacza, że oba argumenty odwoływać się do tego samego obiektu, lub w przypadku typów wartości, mają taką samą wartość.

Przystąpić
źródło
4
Pozwala to uniknąć odpowiedzi na pytanie. Wyjaśniasz, co toEqual()oznacza powiedzenie, że toEqual()sprawdza równoważność , ale oczywiste następne pytanie jest w porządku, więc co oznacza „równoważność”? Opis algorytmu zastosowanego do określenia „równoważności” lub co najmniej przykłady przypadków, w których zachowanie się toEqual()i toBe()różni, uczyniłoby to bardziej użytecznym.
Mark Amery
8
To nie tylko nie odpowiada na pytanie, ale jest błędne . toEqualnależy używać do głębokiego porównywania obiektów, a nie toBe. jsfiddle.net/bBL9P/67
Lloyd Banks
3
Wygląda na to, że ludzie nie zadają sobie trudu, aby sprawdzić, czy to, co mówią, jest poprawne. Zarówno toBe, jak i ToEqual wydają się być ścisłymi porównaniami. Przetestuj ... Więc w moich testach jeszcze nie znalazłem różnicy. na przykład: var f = 1; var g = "1" expect (f == g) .toEqual (true); // true expect (f) .toEqual (g); // false expect (f) .toBe (g); // false
user1809104
6
To jest całkowicie błędne. wcale nietoEqual jest taki sam jak . ==
Meagar
6
Przeczytaj komentarze powyżej. expect(1).toEqual('1')zawodzi, a 1 == '1'jest prawdą. toEqualnie ma z tym nic wspólnego ==. To tak ===, jakby porównywał obiekty w sposób podobny do porównania wartości.
Meagar
33

Cytując projekt jaśminowego githuba:

expect(x).toEqual(y); porównuje obiekty lub prymitywy xiy i przechodzi, jeśli są równoważne

expect(x).toBe(y);porównuje obiekty lub prymitywy xiy i przechodzi, jeśli są one tym samym obiektem

Tharaka
źródło
14

Analiza kodu źródłowego Jasmine rzuca więcej światła na ten problem.

toBejest bardzo prosty i po prostu używa operatora tożsamości / ścisłej równości ===:

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

toEqual, z drugiej strony, ma prawie 150 linii długości i ma specjalną obsługę wbudowanych obiektów, takich jak String,Number , Boolean, Date, Error, Elementi RegExp. W przypadku innych obiektów rekurencyjnie porównuje właściwości.

Jest to bardzo różni się od zachowania operatora równości ==. Na przykład:

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
Tamlyn
źródło
2

toEqual() porównuje wartości w przypadku elementu Primitive lub zawartość w przypadku Objects. toBe()porównuje referencje.

Poniższy kod / zestaw powinien być zrozumiały:

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);
  });
});
Być człowiekiem
źródło
1

Pomyślałem, że ktoś może polubić wyjaśnienie za pomocą (opatrzonego adnotacjami) przykładu:

Poniżej, jeśli moja funkcja deepClone () wykona swoje zadanie poprawnie, test (jak opisano w wywołaniu „it ()”) zakończy się pomyślnie:

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.
    })
})

Oczywiście nie jest to kompletny zestaw testów dla mojej funkcji deepClone (), ponieważ nie testowałem tutaj, czy dosłowność obiektu w tablicy (i ta w niej zagnieżdżona) również ma odrębną tożsamość, ale takie same wartości.

Jared Tomaszewski
źródło
0

Myślę, że toEqual sprawdza głęboką równość, toBe jest tym samym odniesieniem dla 2 zmiennych

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

    expect([]).toEqual([]); // true // deep check
    expect([]).toBe([]); // false
  })
feyzullah yıldız
źródło
-2

Punkty do zapamiętania:

  • toBe()traktuje porównania jak Object.is().
  • toEqual()traktuje porównania jak ===.

Właśnie dlatego dla typów pierwotnych toBei toEqualnie mają dużej różnicy podczas testowania równości, ale dla typów referencyjnych, takich jak obiekty, wolisz używać toEqualdo testowania równości.

John Mutuma
źródło