Lesson 1/9 8 min

Why test?

AI writes code that looks perfect and is wrong. A test is your safety net: it shouts when things break. And the moment a test catches a hallucination.

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 ? »

Prédis avant de lire

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.

🧪 Labo de test · édite le code, lance, observe
Code à tester
Tests
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 :

Sans test, le code de l'IA part directement en production où le bug explose. Avec un test, le bug est attrapé en rouge, corrigé, puis seulement déployé. SANS TEST Code de l'IA a l'air parfait 🚀 Production 💥 -900 € en prod AVEC TEST Code de l'IA a l'air parfait 🧪 Test ✗ attendu 90, reçu -900 🔧 Corrigé test au vert 🚀 Prod sereine
Le test ne ralentit pas : il déplace la découverte du bug du pire moment (la prod) au meilleur (ton éditeur).

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?"

Predict before reading on

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.

🧪 Test lab · edit the code, run, observe
Code under test
Tests
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:

Without a test, the AI's code goes straight to production where the bug blows up. With a test, the bug is caught in red, fixed, then only deployed. WITHOUT A TEST AI's code looks perfect 🚀 Production 💥 -900 € in prod WITH A TEST AI's code looks perfect 🧪 Test ✗ expected 90, got -900 🔧 Fixed test green 🚀 Calm prod
A test doesn't slow you down: it moves the bug's discovery from the worst moment (prod) to the best (your editor).

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
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 ?

Une bonne explication dit : un test attrape les régressions automatiquement, ce qui te permet de modifier le code (ou d'accepter celui de l'IA) sans peur, comme un filet laisse le trapéziste oser plus. Et « ça s'exécute » ne dit rien de la justesse : 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
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 ?)

Un test, c'est du code qui exécute ton code, compare le résultat à ce qu'on attend, et lève une erreur (passe au rouge) si ça ne correspond pas. Le verbe clé, c'est « lever une erreur » : un test ne se contente pas de regarder, il crie quand c'est faux.
⚖️ Juge le code de l'IA
Accepter ou rejeter 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 ?

À rejeter, et c'est un piège classique. Le test passe au vert… parce qu'il fige le bug : il affirme que 100 € avec PROMO10 doit donner -900. Un test qui valide le mauvais résultat est pire que pas de test : il protège le bug. La leçon : un test n'a de valeur que si la valeur attendue est la bonne. Ici, le test devrait exiger .toBe(90) : il doit alors passer au rouge tant que le code n'est pas corrigé.
Le code de l'IA renvoie -900 sans planter. Qu'est-ce que « ça s'exécute sans erreur » prouve ?
À quoi sert un test, en une phrase ?
D'après Simon Willison, quel type d'erreur de l'IA est le PLUS dangereux ?
Tu codes beaucoup avec une IA et tu n'écris aucun test. Quel est le vrai risque ?
Next step

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 →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement