Test JavaScript Jasmine - toBe vs toEqual


348

Disons que j'ai les éléments suivants:

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

Les deux tests ci-dessus réussiront. Y a-t-il une différence entre toBe()et toEqual()quand il s'agit d'évaluer des chiffres? Si oui, quand devrais-je utiliser l'un et pas l'autre?


en bref: aucune différence entre les deux lors de la comparaison des primitives; pour les objets -> toEqual()comparera par clé / contenu de valeurs; toBe()comparera par référence d'objet.
Andre Elrico

Réponses:


488

Pour les types primitifs (par exemple les nombres, les booléens, les chaînes, etc.), il n'y a pas de différence entre toBeet toEqual; soit on va travailler pour 5, trueou "the cake is a lie".

Pour comprendre la différence entre toBeet toEqual, imaginons trois objets.

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

En utilisant une comparaison stricte ( ===), certaines choses sont "les mêmes":

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

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

> c.foo === b.foo
true

Mais certaines choses, même si elles sont «égales», ne sont pas «les mêmes», car elles représentent des objets qui vivent à différents endroits de la mémoire.

> b === c
false

Le toBematcher de Jasmine n'est rien de plus qu'un wrapper pour une comparaison stricte d'égalité

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

c'est la même chose que

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

Ne vous fiez pas seulement à ma parole; voir le code source de toBe .

Mais bet creprésentent des objets fonctionnellement équivalents; ils ressemblent tous les deux

{ foo: { bar: 'baz' } }

Ne serait-il pas formidable de dire cela bet d' cêtre "égaux" même s'ils ne représentent pas le même objet?

Enter toEqual, qui vérifie "l'égalité profonde" (c'est-à-dire effectue une recherche récursive dans les objets pour déterminer si les valeurs de leurs clés sont équivalentes). Les deux tests suivants réussiront:

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

J'espère que cela clarifie certaines choses.


17
"Pour les types primitifs (par exemple les nombres, les booléens, les chaînes, etc.), il n'y a pas de différence entre toBe et toEqual" - car il s'avère que ce n'est pas entièrement vrai. expect(0).toBe(-0)passera mais expect(0).toEqual(-0)échouera.
mgol

11
tl; dr - toBeutilise une égalité stricte - compare par référence, toEqualutilise l'équivalence des propriétés. Recommandé toEqualpour les primitives
Drenai

1
Alors, lequel devrions-nous utiliser pour les primitives, et pourquoi? Drenai, pourquoi recommandez-vous toEqual?
Patrick Szalapski

@PatrickSzalapski Je ne peux que deviner au raisonnement de Denai, mais toEqualest beaucoup plus prudent sur l' égalité ( 0 != -0, "hi" = new String("hi"), etc.), donc je vous recommande d' utiliser toEqual exclusivement à moins que vous êtes réellement préoccupé par l' équivalence de référence. Voir toutes les vérifications effectuées toEqualdans la eqméthode ici: github.com/jasmine/jasmine/blob/master/src/core/matchers/…
River

Je pense qu'il est préférable d'utiliser toBe lors de la comparaison des primitives pour économiser la surcharge qui se fait dans toEqual.
GarfieldKlon

81

toBe()versus toEqual(): toEqual()vérifie l'équivalence. toBe(), d'autre part, s'assure qu'ils sont exactement le même objet.

Je dirais utiliser toBe()lors de la comparaison de valeurs et toEqual()lors de la comparaison d'objets.

Lors de la comparaison des types primitifs, toEqual()et toBe()donnera le même résultat. Lorsque vous comparez des objets, toBe()c'est une comparaison plus stricte, et si ce n'est pas exactement le même objet en mémoire, cela retournera faux. Donc, sauf si vous voulez vous assurer qu'il s'agit exactement du même objet en mémoire, utilisez-le toEqual()pour comparer les objets.

Consultez ce lien pour plus d'informations: http://evanhahn.com/how-do-i-jasmine/

Maintenant, quand on regarde la différence entre toBe()et toEqual()quand il s'agit de chiffres, il ne devrait pas y avoir de différence tant que votre comparaison est correcte. 5sera toujours équivalent à 5.

Un bon endroit pour jouer avec cela pour voir différents résultats est ici

Mise à jour

Un moyen facile de regarder toBe()et toEqual()de comprendre ce qu'ils font exactement en JavaScript. Selon l'API Jasmine, trouvée ici :

toEqual () fonctionne pour les littéraux et variables simples, et devrait fonctionner pour les objets

toBe () se compare à ===

Essentiellement, ce que cela signifie est toEqual()et toBe()sont des ===opérateurs Javascripts similaires , sauf qu'il toBe()vérifie également pour s'assurer qu'il s'agit exactement du même objet, dans celui de l'exemple ci-dessous objectOne === objectTwo //returns falseégalement. Cependant, toEqual()reviendra vrai dans cette situation.

Maintenant, vous pouvez au moins comprendre pourquoi une fois donné:

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

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

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

En effet, comme indiqué dans cette réponse à une question différente mais similaire, l' ===opérateur signifie en fait que les deux opérandes font référence au même objet ou, dans le cas de types de valeur, ont la même valeur.


4
Cela évite de répondre à la question. Vous expliquez ce que toEqual()signifie le fait de toEqual()vérifier l'équivalence , mais la question suivante évidente est correcte, alors que signifie «équivalent»? Une description de l'algorithme utilisé pour déterminer «l'équivalence», ou au moins des exemples de cas où le comportement de toEqual()et toBe()diffèrent, rendrait cela plus utile.
Mark Amery

8
Non seulement cela ne répond pas à la question, mais c'est faux . toEqualdevrait être utilisé pour une comparaison approfondie entre les objets, non toBe. jsfiddle.net/bBL9P/67
Lloyd Banks

3
Il semble que les gens ne prennent pas la peine de vérifier si ce qu'ils disent est correct. ToBe et toEqual semblent être des comparaisons strictes. Testez-le ... Donc, dans mes tests, je n'ai pas encore trouvé de différence. par exemple: var f = 1; var g = "1" expect (f == g) .toEqual (true); // true expect (f) .toEqual (g); // false expect (f) .toBe (g); // false
user1809104

6
C'est complètement faux. toEqualn'est pas du tout le même que ==.
meagar

6
Lisez les commentaires ci-dessus. expect(1).toEqual('1')échoue, alors que 1 == '1'c'est vrai. toEqualn'a rien à voir avec ==. C'est comme ===sauf qu'il comparera les objets d'une manière similaire à la comparaison par valeur.
meagar

33

Pour citer le projet jasmine github,

expect(x).toEqual(y); compare les objets ou primitives x et y et passe s'ils sont équivalents

expect(x).toBe(y);compare les objets ou primitives x et y et passe s'ils sont le même objet


14

En regardant le code source de Jasmine, nous éclairons davantage le problème.

toBeest très simple et utilise simplement l'identité / opérateur d'égalité stricte, ===:

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

toEqual, D'autre part, est près de 150 lignes de long et a un traitement spécial pour les objets comme construit String, Number, Boolean, Date, Error, Elementet RegExp. Pour les autres objets, il compare récursivement les propriétés.

Ceci est très différent du comportement de l'opérateur d'égalité, ==. Par exemple:

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()compare les valeurs si Primitive ou le contenu si Objects. toBe()compare les références.

Le code / suite suivant doit être explicite:

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

1

Je pensais que quelqu'un pourrait aimer une explication par un exemple (annoté):

Ci-dessous, si ma fonction deepClone () fait bien son travail, le test (comme décrit dans l'appel 'it ()') réussira:

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

Bien sûr, ce n'est pas une suite de tests complète pour mon deepClone (), car je n'ai pas testé ici si l'objet littéral dans le tableau (et celui qui y est imbriqué) ont également une identité distincte mais les mêmes valeurs.


0

Je pense que toEqual vérifie profondément égal, toBe est la même référence de 2 variables

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

    expect([]).toEqual([]); // true // deep check
    expect([]).toBe([]); // false
  })

-2

Points à noter:

  • toBe()traite les comparaisons comme comment Object.is().
  • toEqual()traite les comparaisons comme comment ===.

C'est pourquoi pour les types primitifs, toBeet toEqualil n'y a pas beaucoup de différence lors du test d'égalité, mais pour les types de référence comme les objets, vous préférez utiliser toEqualpour tester l'égalité.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.