← Retour au blog
Tests Façade de bâtiment en briques dont toutes les fenêtres ont été brisées les unes après les autres, illustration du Broken Windows Effect.

Un test qui pollue la console devrait échouer : le Broken Windows Effect

Pourquoi un test vert qui pollue stdout/stderr devrait être rouge, et comment le Broken Windows Effect s'applique à la qualité des tests et d'une codebase.

📅 ✍️ Antoine Coulon
testingvitestjestbroken-windowscode-quality

Un test peut être vert et mentir. Il passe, la CI affiche son joli ✅, et pourtant il déverse à chaque exécution une cascade de warnings et d’erreurs dans la console. On a fini par s’y habituer : ce bruit fait partie du décor, on scrolle au-dessus sans plus le voir. C’est exactement là que le problème commence.

Ma position est simple : un test qui réussit (🟢) mais qui pollue la console (stdout ou stderr) devrait être considéré comme un test qui échoue (🔴). Pas par purisme, mais parce que cette pollution est presque toujours le symptôme visible d’un problème bien réel.

Pourquoi un warning ne devrait pas rester sans suite

Quand un test crache du bruit dans la console, il est tentant de hausser les épaules : « il passe, donc tout va bien ». Mais ce bruit a des causes, et il a des coûts.

Aucune de ces raisons n’est dramatique prise isolément. C’est précisément leur accumulation silencieuse qui pose problème, et c’est là qu’intervient une théorie venue d’ailleurs.

Le piège du Broken Windows Effect

Le Broken Windows Effect (l’effet des vitres brisées) vient d’une théorie formulée par George L. Kelling, professeur de criminologie, et James Q. Wilson, professeur de science politique. Leur observation : si une fenêtre d’un bâtiment est cassée et qu’on ne la répare pas, toutes les autres fenêtres finiront bientôt brisées elles aussi.

Le mécanisme est psychologique. Une dégradation laissée en l’état envoie un signal : ici, personne ne veille, personne ne s’en soucie. Dès lors, la dégradation suivante coûte moins cher moralement, et le déclin s’accélère de lui-même.

L’analogie avec les tests est directe. Le premier warning toléré est la première vitre brisée. Tant qu’il reste seul, on pourrait croire qu’il est inoffensif. Mais il fixe une nouvelle norme : puisqu’on accepte ce bruit-là, pourquoi pas le suivant ? De fil en aiguille, la console devient illisible, plus personne ne distingue le signal du bruit, et l’on perd la capacité de réagir quand une vraie alerte apparaît.

Le principe ne s’arrête pas à la suite de tests. Il s’applique plus largement à l’échelle d’une codebase tout entière : un // TODO jamais traité, un any toléré, un lint warning désactivé « provisoirement »… chaque vitre brisée qu’on laisse en place rend la suivante plus facile à accepter. Veiller à ce que ces warnings ne commencent jamais à s’installer, c’est entretenir le bâtiment avant qu’il ne se dégrade.

Deux manières de répondre au bruit

Face à un test qui pollue la console, deux approches s’offrent à vous, et elles ne se valent pas.

Approche 1 : faire taire le symptôme

La première consiste à masquer les logs. On peut monkey patcher console.log et console.error avant un test pour les neutraliser, ou s’appuyer sur les options natives des runners :

jest --silent
vitest --silent

C’est rapide, et la verbosité disparaît. Mais on ne traite ici que le symptôme. Le warning existe toujours ; on a simplement décidé de ne plus le voir. Et en le cachant, on se prive d’une information utile, liée à quelque chose qui finira peut-être par poser de vrais problèmes en production. C’est mettre du ruban adhésif sur le voyant du tableau de bord.

Approche 2 : chercher la cause racine

La seconde approche est celle que je privilégie, dans une logique Lean : remonter aux causes racines (root causes) et comprendre le pourquoi du warning. La plupart du temps, il est là pour une bonne raison. Plutôt que de l’ignorer, on cherche simplement à corriger le problème de fond : la mauvaise utilisation d’API, la dépendance mal injectée, la ressource laissée ouverte. Une fois la cause traitée, le bruit disparaît de lui-même, et pour de bon.

Pour rendre cette discipline impossible à contourner, on peut aller plus loin et forcer les tests à échouer dès que quelque chose est écrit dans stdout ou stderr. Le package vitest-fail-on-console s’intègre à Vitest pour faire exactement cela :

import failOnConsole from "vitest-fail-on-console";

failOnConsole({
  shouldFailOnerror: true,
  shouldFailOnWarn: true,
  shouldFailOnLog: false,
  shouldFailOnInfo: false,
  shouldFailOnDebug: false,
});

À partir de là, un test qui émet un warning ou une erreur dans la console fait passer la suite au rouge : la vitre brisée déclenche immédiatement l’alarme, au lieu d’être tolérée en silence.

Ce principe n’a rien d’isolé ni d’extrême : il est appliqué un peu partout dans l’écosystème des devtools. Rush.js, par exemple, fait échouer la build d’un projet par défaut dès qu’un warning est émis. La logique est toujours la même : ne pas laisser le bruit s’accumuler jusqu’à devenir la norme.

Conclusion

Un test vert qui pollue la console n’est pas un test propre : c’est un test qui réussit malgré un problème qu’il vous montre du doigt. Le Broken Windows Effect rappelle qu’aucune dégradation n’est vraiment isolée : la première vitre brisée que l’on tolère ouvre la voie à toutes les suivantes.

La bonne réponse n’est donc pas de faire taire le bruit, mais d’en éliminer la cause. Et pour tenir cette discipline dans la durée, le plus sûr reste de l’automatiser : faire échouer la suite dès qu’un warning apparaît transforme une bonne intention en garde-fou. Une console silencieuse n’est pas un détail cosmétique : c’est la preuve, à chaque exécution, que rien ne se dégrade en coulisses.