ConstructiCat Logo
CodeBust.
Browse section ▾

Égalité sensible.

Un test vérifie le comportement en comparant la représentation textuelle d'un objet (toString(), JSON.stringify(), HTML rendu) à un littéral de chaîne attendu, couplant ainsi le test à un formatage accessoire plutôt qu'aux valeurs qui comptent réellement.

##Signs and Symptoms

Vous reconnaissez une Égalité sensible lorsque le côté réel d'une assertion sérialise un objet en texte et que son côté attendu est un littéral de chaîne qui regroupe de nombreux champs avec des séparateurs, des guillemets, des crochets et des espaces.

Signes révélateurs :

  • La valeur réelle provient de .toString(), String(x), d'un littéral de gabarit (template literal), de JSON.stringify(...), wrapper.html(), el.outerHTML, ou d'une capture instantanée d'un bloc sérialisé.
  • La valeur attendue est une longue chaîne littérale (souvent collée à partir d'une exécution en échec précédente) et vous ne pouvez pas dire d'un coup d'œil quelle partie correspond à l'élément testé.
  • Le test se casse lors de changements qui n'affectent pas le comportement : clés réordonnées, espaces ajoutés/supprimés, précision numérique, format de date, locale, fuseau horaire, ou ordre d'itération des Map/Set/objets.
// Comparaison d'une forme sérialisée à un littéral de chaîne
expect(JSON.stringify(user)).toBe('{"name":"Bob","age":30}');

// Assertion sur la sortie de toString()
expect(order.toString()).toBe('Order[id=7, total=42.00, items=2]');

// Comparaison du HTML rendu complet à un littéral
expect(wrapper.html()).toBe('<div class="card"><h2>Bob</h2><span>30</span></div>');

// Comparaison de chaînes sensible à la locale/au fuseau horaire
expect(d.toString()).toBe('Mon Jun 15 2026 00:00:00 GMT+0000 (UTC)');

##Reasons for the Problem

Pourquoi cela arrive

  • C'est rapide et facile. Comme le notent van Deursen et al. dans Refactoring Test Code, il est rapide de calculer un résultat réel, de le convertir en chaîne et de le comparer à un littéral de chaîne représentant la valeur attendue — souvent, le littéral est simplement copié à partir d'une exécution en échec. Les outils de capture instantanée (snapshot) rendent cela encore plus tentant.
  • L'objet n'a pas d'égalité significative (equals/comparateur profond) câblée, ou le graphe d'objets est volumineux, de sorte que le sérialiser en chaîne semble plus simple que de faire une assertion champ par champ.

Pourquoi cela nuit

  • Fragilité / faux échecs. La chaîne transporte de nombreux détails sans rapport — virgules, guillemets, espaces, ordre des clés, précision des flottants, locale, fuseau horaire, ordre d'itération des collections. Chaque fois que le format de toString()/sérialisation change, des tests sans rapport commencent à échouer alors même que le comportement est correct. C'est une cause classique de Test fragile.
  • Maintenabilité. Un seul changement de formatage ou de bibliothèque oblige à modifier de nombreux tests, et le gigantesque littéral attendu est fastidieux à mettre à jour et facile à se tromper subtilement.
  • Lisibilité / intention masquée. Un mur de texte comme valeur attendue masque ce qui est réellement vérifié ; un relecteur ne peut pas voir le seul champ qui intéresse le test.
  • Fausse confiance. Un bloc sérialisé en chaîne peut passer pour de mauvaises raisons (deux états distincts qui se sérialisent vers le même texte), et il incite les développeurs à réenregistrer le littéral/la capture en cas d'échec sans vérifier que la nouvelle valeur est réellement correcte.
  • Instabilité (flakiness). Les chaînes sensibles à la locale, au fuseau horaire et à l'ordre font dépendre les résultats de l'environnement dans lequel le test s'exécute.

##Treatment

Faites vos assertions sur les données, et non sur leur rendu.

  1. Comparez les valeurs structurées avec une égalité profonde plutôt qu'avec une égalité de chaîne (toEqual/toStrictEqual), qui est indépendante de l'ordre et des espaces pour les objets.
  2. Ne vérifiez que les attributs pertinents avec des comparateurs partiels (toMatchObject, expect.objectContaining) afin qu'un changement de formatage ailleurs ne puisse pas casser le test.
  3. Analysez, ne comparez pas des chaînes. Lorsque vous recevez une charge utile sérialisée (par exemple, un corps JSON d'API), reconvertissez-la en structure et comparez les structures.
  4. Introduisez une méthode d'égalité/de comparaison sur l'objet (la refactorisation recommandée par van Deursen et al.) et faites une assertion d'égalité d'objets plutôt qu'une égalité de toString().
  5. Si vous devez comparer une sortie sérialisée, canonicalisez-la d'abord — triez les clés, fixez la locale/le fuseau horaire, fixez la précision numérique — et préférez une capture instantanée relue à un littéral en ligne collé à la main.
  6. Pour les tests de DOM/composants, interrogez de manière sémantique (Testing Library getByRole, toHaveTextContent) au lieu de comparer le HTML complet.
// Avant — Égalité sensible
expect(JSON.stringify(user)).toBe('{"name":"Bob","age":30}');

// Après — égalité structurelle (indépendante de l'ordre des clés / des espaces)
expect(user).toEqual({ name: 'Bob', age: 30 });

// Après — assertion uniquement sur le champ testé
expect(user).toMatchObject({ age: 30 });
// Avant — comparaison de chaîne sur une charge utile d'API
expect(res.text).toBe('{"id":7,"total":42}');

// Après — comparaison des structures analysées
expect(JSON.parse(res.text)).toEqual({ id: 7, total: 42 });