Le code qui avait l'air parfait
Vendredi, 17h. Tu construis une petite boutique en ligne et il te manque une fonction : appliquer un code promo « PROMO10 » qui retire 10 % du total. Tu demandes à l'IA. Elle répond en deux secondes, avec un petit commentaire rassurant :
function appliquerCoupon(total, code) {
if (code === 'PROMO10') {
return total - total * 10; // remise de 10 %
}
return total;
}
Le code est propre. Il s'exécute sans la moindre erreur. Tu l'intègres, la page s'affiche, le panier se met à jour, rien ne plante. Tu passes à la suite, tranquille. Tu déploies.
Sauf que pour un panier de 100 €, cette fonction ne renvoie pas 90. Elle renvoie -900. L'IA a écrit total * 10 au lieu de total * 0.10 : au lieu de retirer 10 %, elle retire dix fois le total. Ton client n'a rien vu, toi non plus, parce que ça ne plante pas. Le premier à le remarquer sera l'acheteur qui voit son panier afficher un prix négatif. Ou pire : ta compta, en fin de mois.
Le piège central de tout ce cours : du code qui s'exécute sans erreur n'est pas du code correct. -900, ça ne lève aucune exception, ça ne met aucune ligne en rouge dans la console. Un bug logique ne crie pas tout seul. Il faut quelqu'un (ou quelque chose) pour lui poser la question : « est-ce bien 90 que tu renvoies ? »
Avec le code ci-dessus, qu'est-ce que appliquerCoupon(100, 'PROMO10') va renvoyer ? Et appliquerCoupon(100, '') (sans code promo) ? Écris les deux nombres avant de dérouler.
Voir la réponse
appliquerCoupon(100, 'PROMO10') renvoie -900 : 100 - 100 * 10 = 100 - 1000 = -900 (la multiplication passe avant la soustraction). appliquerCoupon(100, '') renvoie bien 100, car aucun code promo n'est appliqué. Le bug ne touche qu'un seul chemin, et c'est exactement celui que personne n'a vérifié à la main. Dans une minute, tu vas le faire dire « faux » à un test, puis le corriger.
Un test, c'est un filet de sécurité
Pourquoi on ne teste pas, en général ? Par peur de perdre du temps. Mais le vrai temps perdu, c'est l'autre : celui où tu modifies une fonction trois semaines plus tard, où ça casse quelque chose ailleurs, et où tu passes ta soirée à chercher quoi. Un test, c'est ce qui t'évite cette soirée.
L'image juste, c'est le filet du trapéziste. Le filet ne t'empêche pas de sauter : il te permet de sauter plus haut, parce que tu sais que la chute ne sera pas mortelle. Avec des tests, tu refactores sans trembler, tu acceptes du code de l'IA sans prier, tu changes une fonction et tu sais en deux secondes si tu viens de casser le reste.
Concrètement, qu'est-ce qu'un test ? Pas de magie :
Définition : un test, c'est du code qui exécute ton code, compare le résultat à ce que tu attends, et lève une erreur si ça ne colle pas. C'est tout. Une question posée à ton code, automatiquement, à laquelle il répond « vert » (tout va bien) ou « rouge » (quelque chose cloche).
Pour notre fonction, la question est limpide : « si je passe 100 € et le code PROMO10, est-ce que tu me renvoies 90 ? » Écrivons-la, et regardons le code répondre.
À toi : fais passer le test du rouge au vert
Voici le code de l'IA et deux tests, vraiment exécutables, ici, dans ton navigateur. test('nom', () => {...}) décrit un cas ; expect(valeur).toBe(attendu) vérifie. Clique sur Lancer les tests : un test va passer au rouge. Lis le message d'erreur, puis corrige le code (un seul caractère suffit) et relance jusqu'au vert.
Bloqué sur la correction ? Voir le fix
Remplace total * 10 par total * 0.10 (ou total * 0.1). La ligne devient return total - total * 0.10;. Relance : les deux tests passent au vert. Tu viens de transformer une intuition (« ça doit faire 90 ») en une preuve rejouable. Tant que ce test existe, plus personne ne pourra recasser ce calcul sans que le rouge le dise immédiatement.
Règle d'or : si tu ne peux pas dire comment tu sais que ton code marche, c'est que tu ne le sais pas : tu l'espères. Un test, c'est cette preuve, écrite une fois et rejouable à l'infini, gratuitement, à chaque modification.
Coder avec l'IA sans tests, c'est rouler sans phares
Ce bug, * 10 au lieu de * 0.10, est typique de l'IA. Elle ne s'est pas trompée de nom de fonction. Ça, ça aurait planté tout de suite, tu l'aurais vu. Elle a écrit quelque chose de plausible mais faux, qui tourne sans broncher. C'est la catégorie d'erreur la plus dangereuse.
Simon Willison, une référence sur le sujet, le résume bien (mars 2025) : les hallucinations « visibles » de l'IA, comme inventer une fonction qui n'existe pas, sont les moins dangereuses : le code plante immédiatement, tu corriges. Les vraies bombes, ce sont les erreurs de logique : le code s'exécute, le résultat est faux, et rien ne te prévient. Source : simonwillison.net.
Plus tu génères de code vite (et avec l'IA, tu en génères beaucoup), plus tu as besoin d'un filet qui vérifie à ta place. Le test, c'est exactement ça : il relit chaque résultat, inlassablement, sans se lasser ni faire confiance. Voilà le contraste :
Dès maintenant : quand tu demandes du code à l'IA, fais-lui écrire le test en même temps. Mais lis le test toi-même : c'est lui qui dit, noir sur blanc, ce que le code est censé faire. On verra en dernière leçon que l'IA peut aussi tricher sur ses propres tests. D'où l'importance de savoir en lire un.
The code that looked perfect
Friday, 5 p.m. You're building a small online shop and one function is missing: applying a "PROMO10" code that takes 10% off the total. You ask the AI. It answers in two seconds, with a reassuring little comment:
function applyCoupon(total, code) {
if (code === 'PROMO10') {
return total - total * 10; // 10% off
}
return total;
}
The code is clean. It runs without a single error. You wire it in, the page renders, the cart updates, nothing crashes. You move on, relaxed. You deploy.
Except for a 100 € cart, this function doesn't return 90. It returns -900. The AI wrote total * 10 instead of total * 0.10: instead of removing 10%, it removes ten times the total. Your client saw nothing, neither did you, because it doesn't crash. The first to notice will be the shopper staring at a negative price. Or worse: your accountant, at month's end.
The central trap of this whole course: code that runs without errors is not correct code. -900 throws no exception, turns no line red in the console. A logic bug doesn't shout on its own. Someone — or something — has to ask it: "are you really returning 90?"
With the code above, what will applyCoupon(100, 'PROMO10') return? And applyCoupon(100, '') (no promo code)? Write both numbers before expanding.
Show the answer
applyCoupon(100, 'PROMO10') returns -900: 100 - 100 * 10 = 100 - 1000 = -900 (multiplication before subtraction). applyCoupon(100, '') correctly returns 100, since no promo is applied. The bug hits one single path — exactly the one nobody checked by hand. In a minute, you'll make a test say "false", then fix it.
A test is a safety net
Why don't we test, usually? Fear of wasting time. But the real wasted time is the other kind: the evening three weeks later when you tweak a function, it breaks something elsewhere, and you spend hours hunting for what. A test is what spares you that evening.
The right image is the trapeze net. The net doesn't stop you from jumping: it lets you jump higher, because you know the fall won't be fatal. With tests, you refactor without shaking, you accept AI code without praying, you change a function and know in two seconds whether you just broke the rest.
Concretely, what is a test? No magic:
Definition: a test is code that runs your code, compares the result to what you expect, and throws an error if they don't match. That's it. A question asked of your code, automatically, answered "green" (all good) or "red" (something's off).
For our function, the question is plain: "if I pass 100 € and the code PROMO10, do you return 90?" Let's write it, and watch the code answer.
Your turn: take the test from red to green
Here's the AI's code and two real, runnable tests, right here in your browser. test('name', () => {...}) describes a case; expect(value).toBe(expected) checks it. Click Run the tests: one test will turn red. Read the error message, then fix the code (a single character is enough) and rerun until green.
Stuck on the fix? Show it
Replace total * 10 with total * 0.10 (or total * 0.1). The line becomes return total - total * 0.10;. Rerun: both tests go green. You just turned a hunch ("it should be 90") into a replayable proof. As long as this test exists, nobody can break that calculation again without the red telling them instantly.
Golden rule: if you can't say how you know your code works, then you don't know — you hope. A test is that proof, written once and replayed infinitely, for free, on every change.
Coding with AI without tests is driving with no headlights
This bug — * 10 instead of * 0.10 — is typical of AI. It didn't get a function name wrong (that would have crashed right away, you'd have seen it). It wrote something plausible but false, running without a hitch. That's the most dangerous category of error.
Simon Willison, a reference on the topic, puts it well (March 2025): the "visible" AI hallucinations, like inventing a function that doesn't exist, are the least dangerous — the code crashes immediately, you fix it. The real bombs are the logic errors: the code runs, the result is wrong, and nothing warns you. Source: simonwillison.net.
The faster you generate code (and with AI, you generate a lot), the more you need a net that checks in your place. A test is exactly that: it rereads every result, tirelessly, without trusting. Here's the contrast:
A tip you can use today: when you ask the AI for code, ask it to write the test at the same time. But read the test yourself: it states, in black and white, what the code is supposed to do. We'll see in the last lesson that AI can also cheat on its own tests — which is exactly why you need to be able to read one.
🎯 Pratique
S'entraîner (clique pour ouvrir) :
💬 Ré-explique sans regarder
Avec tes mots : pourquoi dit-on qu'un test est un « filet de sécurité » ? Et pourquoi « le code s'exécute sans erreur » ne suffit pas à dire qu'il est correct ?
total - total*10 tourne parfaitement et renvoie -900. Un bug logique ne plante pas ; seul un test qui compare au résultat attendu le révèle.🧠 Rappel libre
Sans remonter : c'est quoi un test, en une seule phrase ? (Le verbe important : il fait quoi quand le résultat est faux ?)
⚖️ Juge le code de l'IA
Tu demandes à l'IA un test pour appliquerCoupon. Elle répond : « Voilà, c'est testé : test('marche', () => { expect(appliquerCoupon(100, 'PROMO10')).toBe(-900); }). Le test passe au vert, c'est bon ! » Tu acceptes, ou tu rejettes ?
.toBe(90) : il doit alors passer au rouge tant que le code n'est pas corrigé.You've seen a test go from red to green. But what does it really do under the hood? In lesson 2, we write a test by hand, with no framework: just an if and an error. You'll understand exactly what test() and expect() do for you.
Lesson 2: Your first test →