ConstructiCat Logo
CodeBust.
Browse section ▾

Assertion redondante.

Une assertion redondante compare une valeur à elle-même, ou à un littéral qui lui est égal par construction : son résultat est donc figé et elle ne peut jamais réellement échouer ni détecter une régression.

##Signs and Symptoms

Une assertion est redondante lorsque son résultat est décidé avant même que le code testé ne s'exécute — les opérandes attendu et effectif sont la même valeur, ou ce sont deux littéraux connus pour être égaux (ou inégaux). Le catalogue xUnit/Test Smells (Peruma et al.) la définit comme « une méthode de test contenant une instruction d'assertion dans laquelle les paramètres attendu et effectif sont identiques », et note que l'assertion est donc « soit toujours vraie, soit toujours fausse ».

Comment le repérer :

  • Un assertEquals/toBe/toEqual dont les deux arguments sont la même expression ou variable.
  • Un littéral booléen asserté contre lui-même, par exemple assertTrue(true) ou expect(true).toBe(true).
  • Une comparaison que le compilateur/linter peut prouver constante, comme expect(x === x).toBe(true).
  • Une « vérification de bon sens » qui répète une constante que vous venez de déclarer au lieu d'exercer le système.
// Smell : le résultat est figé, le code testé n'est jamais sollicité
test('user is active', () => {
  expect(true).toBe(true);            // passe toujours
  const status = 'active';
  expect(status).toBe('active');      // répète le littéral, ne prouve rien
  expect(user.id).toEqual(user.id);   // valeur comparée à elle-même
});

Un indice fiable : vous pouvez supprimer entièrement le code de production et l'assertion reste verte.

##Reasons for the Problem

Pourquoi cela arrive

  • Reste de débogage. Le catalogue note explicitement que ce smell « est introduit par les développeurs à des fins de débogage puis oublié » — un emplacement réservé assertTrue(true) codé en dur qui survit jusque dans le commit.
  • Dérive de copier-coller / refactoring. Une variable est substituée des deux côtés d'un assertEquals, ou une valeur testée est renommée de sorte que l'attendu et l'effectif se réduisent au même symbole.
  • Tautologie par construction. Asserter une valeur contre le littéral dont elle vient d'être affectée, au lieu de la confronter à une attente dérivée de façon indépendante.
  • Théâtre de la couverture. Une assertion est ajoutée uniquement pour satisfaire la règle « chaque test doit asserter », sans vérifier quoi que ce soit de significatif.

Pourquoi c'est nuisible

  • Fausse confiance. Le test est perpétuellement vert et compte dans la taille de la suite et dans la couverture, alors qu'il ne vérifie rien. Il ne peut pas attraper une régression, et masque donc des lacunes dans le filet de sécurité.
  • Fiabilité dénuée de sens. Un test qui ne peut jamais échouer ne fournit aucun signal ; un test toujours faux est un poids mort qu'on finit par ignorer ou marquer skip.
  • Lisibilité. Les lecteurs gaspillent des efforts à reconstituer le comportement attendu à partir d'une assertion qui n'affirme qu'une tautologie ; le test ne documente plus aucune exigence.
  • Maintenabilité. Les assertions redondantes accumulent du bruit, gonflent les métriques et érodent la confiance dans la suite, incitant les gens à cesser de lire les assertions attentivement.

##Treatment

Remplacez la tautologie par une vérification qui relie une valeur attendue connue de façon indépendante au résultat effectif produit par le système sous test.

Étapes :

  1. Identifiez l'assertion à résultat figé — même opérande des deux côtés, ou deux littéraux égaux.
  2. Déterminez l'intention réelle. Quel comportement ce test était-il censé vérifier ? Si aucun, l'assertion (ou le test entier) est mort et doit être supprimé.
  3. Assertez la sortie du SUT, pas l'entrée. Fournissez au système une véritable entrée et comparez son résultat calculé à une valeur attendue codée en dur et calculée à la main — et non à l'une de ses propres entrées/variables.
  4. Gardez l'attendu et l'effectif distincts. Assurez-vous que l'opérande attendu est une constante que vous avez écrite à dessein et que l'opérande effectif est la valeur de retour du code testé (cela corrige aussi le smell connexe du mauvais ordre des arguments).
  5. Réexécutez avec l'implémentation cassée (mutez-la) pour confirmer que l'assertion peut réellement échouer.
// Avant — redondant : le résultat est figé
test('discount', () => {
  const total = 100;
  expect(total).toBe(100);          // répète le littéral
  expect(applyDiscount).toBe(applyDiscount); // valeur comparée à elle-même
});

// Après — significatif : entrée connue -> sortie attendue de façon indépendante
test('applies a 10% discount', () => {
  expect(applyDiscount(100, 0.1)).toBe(90); // résultat du SUT vs attente calculée à la main
});

Si un emplacement réservé comme assertTrue(true) a été laissé après un débogage, supprimez-le ; s'il tenait lieu d'une véritable vérification, écrivez cette vérification.

##Detected by

  • sonar javascript:S5863Une assertion ne doit pas recevoir deux fois le même argument
  • sonar java:S5863Une assertion ne doit pas recevoir deux fois le même argument
  • eslint no-constant-binary-expressionInterdire les expressions où l'opération n'affecte pas la valeur (signale les auto-comparaisons / assertions toujours vraies comme assert(a === a))