{
  "chapter": {
    "id": "tests-debogage",
    "level": "premiere",
    "theme": "Programmation",
    "title": "Tests et débogage",
    "description": "Tester un programme : assertions, jeux d'essais,\ntests unitaires, cas limites. Déboguer : lecture de\nla trace d'erreurs, instructions `print` de\ndébogage, débogueur interactif, isolement de bugs,\nréduction au cas minimal.",
    "parties": [
      {
        "id": "tests",
        "title": "Écrire des tests et des assertions"
      },
      {
        "id": "debogage",
        "title": "Trouver et corriger un bug"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition-test"
      ],
      "title": "Notion de test",
      "partie": "tests",
      "statement": "Qu'appelle-t-on un **test** dans le\ncontexte du logiciel ?",
      "options": [
        {
          "text": "Une vérification automatique du comportement attendu",
          "correct": true,
          "feedback": "Un test fournit une entrée connue\net compare la sortie obtenue à la\nsortie attendue. Cela permet de\ndétecter aussi bien les\nrégressions que les bugs\nnouveaux.\n"
        },
        {
          "text": "Un type particulier de variable",
          "correct": false,
          "feedback": "Un test n'est pas une variable.\nC'est une procédure de\nvérification.\n"
        },
        {
          "text": "Une question à choix multiple posée à l'utilisateur final",
          "correct": false,
          "feedback": "Cette interprétation littérale est\nsans rapport avec la notion\ninformatique de test logiciel,\nqui vise à vérifier le bon\nfonctionnement d'un programme.\n"
        },
        {
          "text": "Un avertissement émis par le langage Python",
          "correct": false,
          "feedback": "Un avertissement est un message\nenvoyé pendant l'exécution, sans\ncomparaison à une valeur attendue.\nCe n'est pas un test.\n"
        }
      ],
      "explanation": "Une bonne pratique consiste à\ntester systématiquement ses\nfonctions, en particulier dans les\nparties critiques du programme. Un\nprojet sans tests est très\nfragile."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "assert"
      ],
      "title": "Instruction assert",
      "partie": "tests",
      "statement": "Que fait l'instruction `assert\ncondition` en Python ?",
      "options": [
        {
          "text": "Si la condition est fausse, elle lève une `AssertionError`",
          "correct": true,
          "feedback": "C'est très utile pour vérifier\ndes invariants. Lorsqu'une\nassertion échoue, le programme\ns'arrête, ce qui permet de\ndétecter les bugs au plus tôt.\n"
        },
        {
          "text": "Elle affiche la condition à l'écran",
          "correct": false,
          "feedback": "L'instruction `assert` n'affiche\nrien lorsque la condition est\nvraie. Elle ne lève une erreur\nque dans le cas contraire.\n"
        },
        {
          "text": "Elle convertit la condition en une valeur booléenne",
          "correct": false,
          "feedback": "La condition est déjà évaluée\ncomme un booléen avant l'appel à\n`assert`. La conversion en tant\nque telle se fait avec\n`bool(...)`.\n"
        },
        {
          "text": "Elle ne fonctionne qu'en mode débogage",
          "correct": false,
          "feedback": "Cette description est inexacte. Les assertions sont vérifiées lors\nd'une exécution ordinaire du programme, pas seulement pendant une\nphase de mise au point.\n"
        }
      ],
      "explanation": "Un usage typique :\n`assert n > 0, \"n doit être\npositif\"`. Le message après la\nvirgule apparaît dans l'erreur\naffichée, ce qui aide au\ndébogage."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "jeu-de-tests"
      ],
      "title": "Ce que vérifie un test de fonction",
      "partie": "tests",
      "statement": "Élise écrit des assertions qui appellent uniquement sa fonction\n`moyenne`, sur quelques listes choisies à l'avance. Que vérifient\nces tests ?",
      "options": [
        {
          "text": "Que `moyenne` est correcte pour toutes les listes possibles",
          "correct": false,
          "feedback": "Erreur : un jeu de tests ne vérifie que les cas écrits. Son succès\nne garantit pas que la fonction est correcte pour toutes les\nlistes.\n"
        },
        {
          "text": "Le fonctionnement du programme entier qui utilise `moyenne`",
          "correct": false,
          "feedback": "Erreur : ces assertions n'appellent que `moyenne`. Le reste du\nprogramme n'est pas exécuté, il faudrait d'autres tests pour le\nvérifier.\n"
        },
        {
          "text": "La vitesse d'exécution de la fonction `moyenne`",
          "correct": false,
          "feedback": "Erreur : une assertion compare des valeurs, elle ne mesure aucune\ndurée. Ces tests portent sur les résultats renvoyés.\n"
        },
        {
          "text": "Le comportement de `moyenne` seule, sur les cas choisis",
          "correct": true,
          "feedback": "Bonne réponse : tester une fonction isolément permet de savoir\nprécisément laquelle se comporte mal quand un test échoue. Les\ntests restent limités aux cas choisis.\n"
        }
      ],
      "explanation": "Tester chaque fonction séparément, sur des cas bien choisis (liste\nd'un seul élément, valeurs égales, etc.), permet de localiser\nrapidement un bug. Ces tests ne prouvent pas la correction : ils\naugmentent la confiance."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "exemple-test"
      ],
      "title": "Exemple de test",
      "partie": "tests",
      "statement": "Quelle ligne **teste** que `carre(3)\n== 9` ?",
      "options": [
        {
          "text": "`if carre(3) != 9: print(\"erreur\")`",
          "correct": false,
          "feedback": "Cette ligne effectue bien une\nvérification, mais elle\nn'interrompt pas le programme\nen cas d'échec. L'instruction\n`assert` est plus idiomatique\ndans un contexte de test.\n"
        },
        {
          "text": "`assert carre(3) == 9`",
          "correct": true,
          "feedback": "Si `carre(3)` ne vaut pas $9$,\nl'assertion échoue et le\nprogramme s'arrête. Sinon, le\nprogramme continue\nsilencieusement.\n"
        },
        {
          "text": "`carre(3)`",
          "correct": false,
          "feedback": "Cette ligne exécute simplement\nla fonction sans rien vérifier.\nAucune comparaison n'est\neffectuée.\n"
        },
        {
          "text": "`test carre(3) == 9`",
          "correct": false,
          "feedback": "Cette syntaxe n'existe pas en\nPython. Le mot-clé `test`\nn'est pas reconnu par le\nlangage.\n"
        }
      ],
      "explanation": "L'instruction `assert` est rapide à écrire et lisible. On peut\najouter après la condition un message affiché en cas d'échec :\n`assert carre(3) == 9, \"carre(3) devrait valoir 9\"`."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "debogage-erreur"
      ],
      "title": "Erreur Python",
      "partie": "debogage",
      "statement": "Quand Python lève une **erreur**, où\nregarder pour comprendre ce qui se\npasse ?",
      "options": [
        {
          "text": "Le code source du module Python lui-même",
          "correct": false,
          "feedback": "Ce serait à faire en dernier\nrecours. La plupart du temps,\nl'erreur vient du code\nutilisateur, pas de\nl'interpréteur.\n"
        },
        {
          "text": "Les paramètres de configuration du système d'exploitation",
          "correct": false,
          "feedback": "La trace d'erreur Python est\nlocale au programme. Le\nsystème d'exploitation n'est\npas concerné.\n"
        },
        {
          "text": "Sur Internet, en cherchant un message proche",
          "correct": false,
          "feedback": "Cette démarche peut compléter\nl'analyse, mais ce n'est pas\nla première chose à faire. Il\nfaut commencer par la trace\nd'erreur affichée par Python.\n"
        },
        {
          "text": "La trace d'erreur, qui indique le fichier et la ligne",
          "correct": true,
          "feedback": "On lit la trace d'erreur du bas\nvers le haut. La dernière ligne\ndonne en général le type\nd'erreur et le message\nassocié.\n"
        }
      ],
      "explanation": "Quelques erreurs fréquentes :\n`NameError` (variable inconnue),\n`TypeError` (incompatibilité de\ntypes), `IndexError` (indice hors\ndes bornes), `ValueError` (valeur\ninappropriée pour une opération)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "print-debug"
      ],
      "title": "Instruction print de débogage",
      "partie": "debogage",
      "statement": "À quoi sert un appel à `print` dans\nun objectif de **débogage** ?",
      "options": [
        {
          "text": "À empêcher d'écrire d'autres lignes de code",
          "correct": false,
          "feedback": "Cette description ne correspond\nà aucun usage réel de `print`.\n"
        },
        {
          "text": "À encoder automatiquement des données en hexadécimal",
          "correct": false,
          "feedback": "La fonction `print` ne réalise\naucune conversion automatique\nen hexadécimal.\n"
        },
        {
          "text": "À afficher la valeur d'une variable à un moment précis",
          "correct": true,
          "feedback": "C'est une technique simple mais efficace. Pour suivre la valeur de\n`x` à l'intérieur d'une boucle, on peut écrire `print(\"x =\", x)`.\n"
        },
        {
          "text": "À ralentir intentionnellement le programme",
          "correct": false,
          "feedback": "Un excès de `print` peut\neffectivement ralentir un\nprogramme, mais ce n'est jamais\nl'objectif recherché.\n"
        }
      ],
      "explanation": "Avantages : la fonction `print` est simple et fonctionne partout.\nInconvénient : il faut penser à retirer ces affichages après le\ndébogage."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "bug-definition"
      ],
      "title": "Notion de bug",
      "partie": "debogage",
      "statement": "Qu'est-ce qu'un **bug** ?",
      "options": [
        {
          "text": "Un comportement non conforme aux spécifications",
          "correct": true,
          "feedback": "Le terme est historique : il\nprovient d'un véritable insecte\nretrouvé en $1947$ dans les\nrelais d'une machine analysée\npar Grace Hopper. La trace de\ncet incident est encore\nconservée.\n"
        },
        {
          "text": "Une fonctionnalité que l'utilisateur final n'a pas demandée",
          "correct": false,
          "feedback": "Cette description est une\nplaisanterie courante (« ce\nn'est pas un bug, c'est une\nfonctionnalité »), mais elle\nne correspond pas à la\ndéfinition technique.\n"
        },
        {
          "text": "Une nouvelle version d'un logiciel",
          "correct": false,
          "feedback": "Une nouvelle version peut\ncomporter des bugs, mais elle\nn'est pas elle-même un bug.\n"
        },
        {
          "text": "Un type particulier de mémoire",
          "correct": false,
          "feedback": "Un bug n'a aucun rapport avec\nla mémoire d'un ordinateur.\n"
        }
      ],
      "explanation": "Il faut distinguer plusieurs\nnotions : un **bug** signifie\nque le code ne fait pas ce qu'il\ndoit ; une **erreur d'exécution**\nsurvient quand le programme\nplante ; une **erreur de spécification** signifie que la\ndéfinition même du\ncomportement attendu est\nincorrecte. Cette dernière est\nla plus difficile à corriger."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "jeu-essai"
      ],
      "title": "Jeu d'essais",
      "partie": "tests",
      "statement": "Qu'est-ce qu'un **jeu d'essais** ?",
      "options": [
        {
          "text": "Un test choisi au hasard",
          "correct": false,
          "feedback": "Un jeu d'essais est conçu de\nmanière réfléchie, et non tiré\nau hasard.\n"
        },
        {
          "text": "Des entrées prévues avec leur sortie attendue",
          "correct": true,
          "feedback": "Un bon jeu d'essais couvre les\ncas normaux, les cas limites\n(par exemple liste vide, liste\nd'un seul élément, valeurs\nextrêmes) et les cas d'erreur\n(entrée invalide). Plus la\ncouverture est large, plus le\ntest est robuste.\n"
        },
        {
          "text": "Un manuel complet d'utilisation du programme et de ses options",
          "correct": false,
          "feedback": "La documentation et les jeux\nd'essais sont deux notions\ndistinctes.\n"
        },
        {
          "text": "Une collection de jeux vidéo",
          "correct": false,
          "feedback": "Cette interprétation littérale\ndu mot « jeu » est sans\nrapport avec son sens\ninformatique.\n"
        }
      ],
      "explanation": "Un bon jeu d'essais détecte les\nrégressions. Si l'on modifie le\ncode et que tous les tests\ncontinuent à passer, on peut\navoir confiance. Si un test\néchoue, on sait précisément quoi\ncorriger."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "exemple-debug"
      ],
      "title": "Lire une trace d'erreur",
      "partie": "debogage",
      "statement": "```\nFile \"main.py\", line 5, in <module>\n    print(liste[5])\nIndexError: list index out of range\n```\nQue dit cette trace d'erreur ?",
      "options": [
        {
          "text": "On accède à `liste[5]`, un indice qui n'existe pas",
          "correct": true,
          "feedback": "Le message\n`IndexError: list index out of\nrange` signifie un accès à un\nindice hors des bornes. La\ncause typique est une mauvaise\nestimation de la taille de la\nliste.\n"
        },
        {
          "text": "La ligne $5$ contient une variable non définie ailleurs",
          "correct": false,
          "feedback": "Cette description correspond à\nune `NameError`, et non à une\n`IndexError`.\n"
        },
        {
          "text": "Il s'agit d'une erreur de syntaxe",
          "correct": false,
          "feedback": "Une erreur de syntaxe se\nprésente sous la forme d'une\n`SyntaxError`, et non d'une\n`IndexError`.\n"
        },
        {
          "text": "Aucun problème n'est signalé",
          "correct": false,
          "feedback": "La trace contient bien une\nerreur, indiquée par\n`IndexError`.\n"
        }
      ],
      "explanation": "Le réflexe à acquérir : lire le\n**type** de l'erreur (qui donne\nla nature du bug), puis la\n**ligne** indiquée (qui en\ndonne l'emplacement). C'est\nsystématique."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "isoler-bug"
      ],
      "title": "Isoler un bug",
      "partie": "debogage",
      "statement": "Pour comprendre l'origine d'un bug\ndans un programme, quelle démarche\nest la plus efficace en première\napproche ?",
      "options": [
        {
          "text": "Ignorer le bug s'il n'apparaît qu'à l'occasion",
          "correct": false,
          "feedback": "Un bug intermittent, surtout,\nne s'efface pas en l'ignorant.\nIl finit par réapparaître au\nplus mauvais moment. Au\ncontraire, il faut chercher\nactivement à le reproduire.\n"
        },
        {
          "text": "Réduire l'entrée au cas le plus simple qui reproduit le bug",
          "correct": true,
          "feedback": "C'est l'idée du cas de test\nminimal. Si un programme plante\nsur une liste de mille\néléments, essayer d'abord avec\ndix, puis avec cinq, puis avec\ndeux, en gardant à chaque fois\nla propriété qui fait planter.\nOn obtient un exemple court qui\naide à localiser le bug et à\nécrire un test de régression.\n"
        },
        {
          "text": "Réécrire l'intégralité du programme depuis zéro",
          "correct": false,
          "feedback": "Cette démarche est risquée et\npeu économique. Elle introduit\nsouvent de nouveaux bugs sans\ncomprendre l'origine du bug\ninitial.\n"
        },
        {
          "text": "Multiplier la taille de l'entrée jusqu'à trouver une régularité",
          "correct": false,
          "feedback": "C'est l'inverse de la démarche\nutile. Plus l'entrée est\ngrande, plus il est difficile\nd'identifier ce qui déclenche\nprécisément le bug.\n"
        }
      ],
      "explanation": "La discipline du cas de test\nminimal est l'une des bases du\ndébogage. Elle permet aussi\nd'écrire un test de non-régression\ncompact, qui protégera contre la\nréapparition du bug à l'avenir."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "trace-erreur"
      ],
      "title": "Identifier un bug",
      "partie": "debogage",
      "statement": "```python\ndef somme(liste):\n    total = 0\n    for i in range(1, len(liste)):\n        total += liste[i]\n    return total\n```\nQuel **bug** contient cette fonction ?",
      "options": [
        {
          "text": "Aucun bug, la fonction additionne bien tous les éléments",
          "correct": false,
          "feedback": "Erreur : pour `[1, 2, 3]`, la fonction\nrenvoie 5 et non 6. Le premier élément\nn'est jamais ajouté.\n"
        },
        {
          "text": "La fonction ne renvoie jamais la moindre valeur utile",
          "correct": false,
          "feedback": "La fonction se termine bien\npar `return total`, donc\nelle renvoie une valeur.\n"
        },
        {
          "text": "La syntaxe de `range` est incorrecte avec deux arguments",
          "correct": false,
          "feedback": "Erreur : `range(1, n)` est valide (début\ninclus, fin exclue). Le code s'exécute\nsans erreur, le bug est purement logique.\n"
        },
        {
          "text": "Le premier élément, `liste[0]`, n'est jamais ajouté",
          "correct": true,
          "feedback": "Bonne réponse : la boucle commence à\nl'indice $1$ au lieu de $0$. C'est un bug\nclassique de **décalage d'indice**. Pour\nla liste `[1, 2, 3]`, la\nfonction renvoie $5$ au lieu\nde $6$. Un test sur un cas\nsimple permettrait de le\ndétecter.\n"
        }
      ],
      "explanation": "Les bugs de décalage d'indice\nsont très fréquents. Il faut\ntoujours tester avec des listes\nsimples (`[]`, `[1]`,\n`[1, 2]`) pour les détecter.\nUn bon test serait par exemple\n`assert somme([1, 2, 3]) == 6`."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "traceback-lecture"
      ],
      "title": "Lire la trace d'erreur",
      "partie": "debogage",
      "statement": "Dans une trace d'erreur à plusieurs\nniveaux, où trouve-t-on en général\nla **cause initiale** du bug ?",
      "options": [
        {
          "text": "Quelque part au milieu de la trace d'erreur affichée",
          "correct": false,
          "feedback": "Aucune position particulière\nau milieu n'est privilégiée.\nLa cause initiale se lit en\nbas de la trace.\n"
        },
        {
          "text": "Au hasard dans la trace",
          "correct": false,
          "feedback": "La position du message\nd'erreur n'a rien d'aléatoire :\nil se trouve toujours à la\nfin de la trace.\n"
        },
        {
          "text": "En haut de la trace",
          "correct": false,
          "feedback": "C'est plutôt l'inverse. Le\nmessage d'erreur le plus\nprécis se trouve en bas de\nla trace.\n"
        },
        {
          "text": "La dernière ligne (en bas), avec le type d'erreur",
          "correct": true,
          "feedback": "Python affiche la pile des\nappels de fonctions dans\nl'ordre chronologique, et le\nmessage d'erreur tout en\nbas. La ligne la plus utile\nest généralement celle qui\nrenvoie à votre propre code.\n"
        }
      ],
      "explanation": "Pour lire une trace : commencer\npar la fin, qui indique le type\nd'erreur, puis remonter\njusqu'au code qu'on a soi-même\nécrit. Les premières lignes,\nsouvent dans des bibliothèques,\nsont rarement le bon point\nd'entrée."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "reproductibilite"
      ],
      "title": "Reproduire un bug",
      "partie": "tests",
      "statement": "Pour résoudre un bug, qu'est-ce qui\nest **essentiel** à faire en\npremier ?",
      "options": [
        {
          "text": "Demander à un collègue de regarder le code",
          "correct": false,
          "feedback": "Cela peut aider dans un\nsecond temps, mais ni un\nhumain ni un assistant\nautomatique ne peuvent\ndiagnostiquer un bug sans\npouvoir le reproduire.\n"
        },
        {
          "text": "Réécrire le code suspect",
          "correct": false,
          "feedback": "La réécriture n'est pas la\npremière étape. Il faut\nd'abord pouvoir observer le\nbug pour pouvoir vérifier\nensuite la correction.\n"
        },
        {
          "text": "Réinstaller l'interpréteur Python",
          "correct": false,
          "feedback": "Cette opération est rarement\nnécessaire. La cause d'un\nbug se trouve presque\ntoujours dans le code lui-même.\n"
        },
        {
          "text": "Le reproduire avec une entrée minimale fiable",
          "correct": true,
          "feedback": "Un bug intermittent est très\ndifficile à corriger. La\nreproduction est la première\nétape, et permet d'écrire un\n**test de régression**.\n"
        }
      ],
      "explanation": "L'adage classique est :\n« si l'on ne peut pas\nreproduire le bug, on ne peut\npas le corriger ». Une fois la\nreproduction obtenue, on peut\nréduire l'entrée au cas minimal\nqui déclenche encore le bug,\npuis corriger."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "debogueur"
      ],
      "title": "Débogueur",
      "partie": "debogage",
      "statement": "Qu'est-ce qu'un **débogueur** ?",
      "options": [
        {
          "text": "Un compilateur qui traduit le code source en exécutable",
          "correct": false,
          "feedback": "Un compilateur transforme du\ncode source en code\nexécutable. Il n'a aucun\nrapport avec le débogage\ninteractif.\n"
        },
        {
          "text": "Un outil qui exécute pas à pas et montre les variables",
          "correct": true,
          "feedback": "Bonne réponse : on avance ligne par ligne et on inspecte l'état du\nprogramme. Les environnements de programmation en proposent\nsouvent un, très utile pour comprendre un bug.\n"
        },
        {
          "text": "Un antivirus qui détecte les programmes malveillants",
          "correct": false,
          "feedback": "Un antivirus protège contre\nles logiciels malveillants ;\nil n'a rien à voir avec le\ndébogage de programmes.\n"
        },
        {
          "text": "Un fichier de journalisation des erreurs du programme",
          "correct": false,
          "feedback": "Un débogueur est un outil\ninteractif, et non un fichier\nstocké sur le disque.\n"
        }
      ],
      "explanation": "Un débogueur permet d'arrêter le programme sur une ligne choisie,\nde l'exécuter ensuite instruction par instruction et d'observer à\nchaque étape la valeur des variables, sans modifier le code."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "precondition",
        "assert",
        "specification"
      ],
      "title": "Une assertion en tête de fonction",
      "partie": "tests",
      "statement": "```python\ndef moyenne(notes):\n    assert len(notes) > 0\n    total = 0\n    for n in notes:\n        total = total + n\n    return total / len(notes)\n```\nQuel est le rôle de l'assertion placée au début de la fonction ?",
      "options": [
        {
          "text": "Vérifier une précondition : la liste reçue n'est pas vide",
          "correct": true,
          "feedback": "Bonne réponse : l'assertion porte sur l'argument, avant tout\ncalcul. Si on appelle `moyenne([])`, le programme s'arrête avec\nune `AssertionError` au lieu d'effectuer une division par zéro.\n"
        },
        {
          "text": "Vérifier une postcondition sur la valeur renvoyée",
          "correct": false,
          "feedback": "Erreur : une postcondition porte sur le résultat, après le calcul.\nCette assertion est vérifiée au début, sur l'argument `notes`,\navant que la moyenne soit calculée.\n"
        },
        {
          "text": "Faire renvoyer $0$ à la fonction quand la liste est vide",
          "correct": false,
          "feedback": "Erreur : une assertion ne renvoie aucune valeur. Si la condition\nest fausse, elle arrête le programme avec une `AssertionError`.\n"
        },
        {
          "text": "Afficher un avertissement puis poursuivre le calcul",
          "correct": false,
          "feedback": "Erreur : une assertion n'affiche rien quand la condition est\nvraie, et arrête le programme quand elle est fausse. Le calcul ne\nse poursuit pas.\n"
        }
      ],
      "explanation": "Une précondition décrit ce que la fonction attend de ses arguments ;\nune postcondition décrit ce qu'elle garantit sur son résultat. Une\nassertion placée en tête de fonction vérifie la précondition à\nchaque appel."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "jeu-de-tests",
        "correction-bug"
      ],
      "title": "Garder le test qui a révélé un bug",
      "partie": "tests",
      "statement": "Nour a corrigé sa fonction `remise`, dont le bug avait été révélé\npar l'assertion `assert remise(100) == 90`. Que faire de cette\nassertion maintenant que le test passe ?",
      "options": [
        {
          "text": "La supprimer, puisque le bug est corrigé",
          "correct": false,
          "feedback": "Erreur : une modification ultérieure du code pourrait réintroduire\nle bug. Sans ce test, rien ne permettrait de s'en apercevoir.\n"
        },
        {
          "text": "La garder dans le jeu de tests",
          "correct": true,
          "feedback": "Bonne réponse : ce cas a déjà mis un bug en évidence. Le conserver\npermet de détecter immédiatement le retour de ce bug après une\nfuture modification.\n"
        },
        {
          "text": "La remplacer par `print(remise(100))`",
          "correct": false,
          "feedback": "Erreur : un affichage ne vérifie rien automatiquement. Il faudrait\nrelire la valeur à chaque exécution pour repérer une erreur.\n"
        },
        {
          "text": "La réécrire avec la valeur que renvoie la fonction aujourd'hui",
          "correct": false,
          "feedback": "Erreur : un test compare le résultat à la valeur attendue d'après\nla spécification, pas à ce que renvoie le code actuel. Sinon il\npasserait même si la fonction était fausse.\n"
        }
      ],
      "explanation": "Un jeu de tests s'enrichit au fil de la mise au point : chaque bug\ncorrigé laisse derrière lui un test qui l'aurait détecté. La qualité\net le nombre des tests comptent autant que leur succès."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "tests-en-amont"
      ],
      "title": "Écrire les tests avant le code",
      "partie": "tests",
      "statement": "Quel est l'avantage principal de la démarche qui consiste à écrire\nles tests **avant** le code ?",
      "options": [
        {
          "text": "Elle force à clarifier la spécification avant de coder",
          "correct": true,
          "feedback": "On écrit la spécification\nsous une forme exécutable\n(le test), ce qui contraint\nà réfléchir précisément\naux comportements attendus\navant de coder.\n"
        },
        {
          "text": "Le code est plus rapide à écrire dès le départ",
          "correct": false,
          "feedback": "Cette approche n'est pas\nplus rapide au démarrage,\net exige même un certain\ntemps d'apprentissage. Son\nintérêt se mesure surtout\nà moyen terme, sur la\nqualité et la fiabilité\ndu code produit.\n"
        },
        {
          "text": "Elle dispense totalement d'avoir à écrire des commentaires",
          "correct": false,
          "feedback": "Aucun rapport. Les\ncommentaires gardent leur\nutilité pour expliquer\nles intentions et les\nchoix de conception.\n"
        },
        {
          "text": "Elle permet d'éviter complètement les bugs",
          "correct": false,
          "feedback": "Cette affirmation est trop\noptimiste. Aucune méthode\nne supprime tous les bugs.\nLes tests réduisent leur\nfréquence et facilitent\nleur détection précoce.\n"
        }
      ],
      "explanation": "Écrire d'abord les tests oblige à fixer les résultats attendus, y\ncompris sur les cas limites comme une liste vide : c'est une\nspécification précise que le code devra respecter. Le succès des\ntests ne garantit pas pour autant la correction du programme."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "bug-difficile"
      ],
      "title": "Bug intermittent",
      "partie": "debogage",
      "statement": "Un bug se produit **parfois**,\nsans que l'on sache prévoir\nquand. Que faire ?",
      "options": [
        {
          "text": "Relever les données de chaque exécution pour le reproduire",
          "correct": true,
          "feedback": "Un bug qui ne se produit que parfois dépend souvent d'une donnée\nqui change d'une exécution à l'autre, comme un tirage au hasard ou\nune saisie. En notant ces données, on retrouve le cas qui déclenche\nle bug et on peut le reproduire.\n"
        },
        {
          "text": "Relancer le programme jusqu'à ce que le bug ne se produise plus",
          "correct": false,
          "feedback": "Erreur : si le bug ne se\nmanifeste pas lors d'une\nexécution donnée, il n'a pas\ndisparu pour autant. Sans\ndiagnostic de la cause, il\nfinira par resurgir.\n"
        },
        {
          "text": "Ralentir l'exécution avec des appels à `time.sleep()`",
          "correct": false,
          "feedback": "Erreur : ralentir le programme ne change pas les données qu'il\ntraite. Si le bug dépend d'une valeur tirée au hasard ou saisie, il\nreste présent et resurgira.\n"
        },
        {
          "text": "Ignorer le bug",
          "correct": false,
          "feedback": "C'est une mauvaise pratique.\nUn bug ignoré finit\ntoujours par revenir, et\nsouvent au pire moment.\n"
        }
      ],
      "explanation": "Causes fréquentes d'un bug intermittent : une valeur tirée au\nhasard avec le module `random`, une saisie de l'utilisateur, le\ncontenu d'un fichier lu. Une fois le cas fautif retrouvé, on\nl'ajoute au jeu de tests."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "debug-strategie"
      ],
      "title": "Stratégie globale",
      "partie": "debogage",
      "statement": "Devant un bug, quelle est la\n**bonne stratégie** générale à\nadopter ?",
      "options": [
        {
          "text": "Demander une mise à jour du langage Python",
          "correct": false,
          "feedback": "Une telle mise à jour est\nextrêmement rarement la\nsolution à un bug\napplicatif.\n"
        },
        {
          "text": "Reproduire, isoler, corriger, puis ajouter un test",
          "correct": true,
          "feedback": "C'est une méthodologie\néprouvée. Sauter une étape,\nen particulier la dernière,\ngarantit que le bug\nfinira par réapparaître.\n"
        },
        {
          "text": "Réécrire intégralement le programme",
          "correct": false,
          "feedback": "Cette démarche est risquée :\nelle peut introduire de\nnouveaux bugs et masque\nl'origine du problème\ninitial.\n"
        },
        {
          "text": "Désactiver les tests existants",
          "correct": false,
          "feedback": "Cette démarche est\ncontre-productive. Les\ntests sont précisément\nl'outil principal pour\ndétecter et corriger les\nbugs.\n"
        }
      ],
      "explanation": "Discipline : ne jamais\ncorriger un bug sans avoir\nréussi à le reproduire, et\nne jamais corriger sans\najouter ensuite un test de\nrégression. Sinon, le bug\nfinira par réapparaître,\net le temps gagné au\ndépart sera perdu plus\ntard."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "piege-tests"
      ],
      "title": "Test mal écrit",
      "partie": "tests",
      "statement": "Quel est le problème avec ce\ntest ?\n```python\nassert chercher([1, 2, 3], 2)\n```",
      "options": [
        {
          "text": "La syntaxe de `assert` est invalide sans message d'erreur",
          "correct": false,
          "feedback": "Erreur : le message après la virgule est\nfacultatif, `assert expression` seul est\nvalide. Le défaut est sémantique : le test\nn'est pas assez précis.\n"
        },
        {
          "text": "La fonction `chercher` n'existe peut-être pas encore",
          "correct": false,
          "feedback": "On suppose que la fonction\nexiste et est définie\nquelque part dans le code.\nLe problème ne tient pas\nà son existence.\n"
        },
        {
          "text": "Aucun problème, le test vérifie que 2 est trouvé à l'indice 1",
          "correct": false,
          "feedback": "Erreur : le test ne compare jamais la\nvaleur renvoyée à 1. Il passe pour tout\nrésultat « vrai », par exemple 2 ou `True`.\n"
        },
        {
          "text": "Il vérifie seulement que le résultat est « vrai »",
          "correct": true,
          "feedback": "Bonne réponse : ce test est trop laxiste.\nTout résultat vrai au sens booléen (1, 2,\n`True`...) le fait passer, indice faux\ncompris. On préfère écrire\n`assert chercher([1, 2, 3],\n2) == 1`, qui vérifie\nprécisément la valeur\nrenvoyée.\n"
        }
      ],
      "explanation": "Piège des tests laxistes :\nils passent quand ils ne\ndevraient pas, et laissent\npasser des bugs. Il faut\ntoujours **vérifier des valeurs précises**, et non\nsimplement l'absence\nd'erreur."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "test-vs-debug"
      ],
      "title": "Tests et débogage",
      "partie": "debogage",
      "statement": "Quelle est la\n**complémentarité** entre tests\net débogage ?",
      "options": [
        {
          "text": "L'une remplace l'autre",
          "correct": false,
          "feedback": "Ces activités sont\ncomplémentaires : ni les\ntests ni le débogage ne\npeuvent se passer l'un de\nl'autre dans un projet\nsérieux.\n"
        },
        {
          "text": "Les tests trouvent les bugs, le débogage les corrige",
          "correct": true,
          "feedback": "Ce sont deux phases\ndistinctes mais\ncomplémentaires. Tester,\nc'est investir pour\ndétecter ; déboguer, c'est\ninvestiguer pour corriger.\n"
        },
        {
          "text": "Le débogage permet d'éviter complètement d'écrire des tests",
          "correct": false,
          "feedback": "C'est l'inverse : un bon\nensemble de tests permet\nde détecter rapidement un\nbug et de réduire le\ntemps passé à le déboguer.\n"
        },
        {
          "text": "Les deux activités sont identiques",
          "correct": false,
          "feedback": "Ces activités diffèrent\npar leur objectif et leur\ndéroulement, comme\nexpliqué dans la bonne\nréponse.\n"
        }
      ],
      "explanation": "Une démarche idéale combine\nles deux activités : on\nécrit d'abord des tests qui\ntrouvent un bug ; on\ndébogue ensuite pour\nlocaliser, comprendre et\ncorriger ; on ajoute enfin\nun test de régression pour\nempêcher la résurgence du\nbug."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "tests",
      "statement": "Parmi les affirmations suivantes\nsur les tests et le débogage,\nlaquelle est **fausse** ?",
      "options": [
        {
          "text": "Réduire l'entrée problématique au cas le plus simple qui reproduit le bug aide à localiser sa cause",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est l'une des\ntechniques de base pour\nisoler un bug.\n"
        },
        {
          "text": "La trace d'erreur indique le type d'erreur et la ligne où elle s'est produite",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est même son\nrôle principal.\n"
        },
        {
          "text": "Un test unitaire vérifie une seule fonction de manière isolée",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est même la\ndéfinition d'un test\nunitaire.\n"
        },
        {
          "text": "Les tests garantissent l'absence de bugs",
          "correct": true,
          "feedback": "Cette affirmation est\nfausse (donc c'est la\nbonne réponse). Les\ntests révèlent la\n**présence** de bugs sur\nles cas effectivement\ntestés, mais ils ne\npeuvent jamais prouver\nl'**absence** de bugs sur\ndes entrées non testées.\nEdsger Dijkstra a résumé\ncela par : « Tester ne\npeut prouver l'absence\nde bugs ».\n"
        }
      ],
      "explanation": "Citation d'Edsger Dijkstra :\n« Program testing can be\nused to show the presence of\nbugs, but never to show\ntheir absence. » Pour\ngarantir formellement\nl'absence de bugs, il\nfaudrait recourir à une\npreuve mathématique, ce qui\nreste rarement praticable\nsur du code réel."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "types-erreurs"
      ],
      "title": "Trois familles d'erreurs",
      "partie": "debogage",
      "statement": "On distingue habituellement les\nerreurs de **syntaxe**, les\nerreurs **d'exécution** et les\nerreurs **sémantiques** (ou\nlogiques). Quelle catégorie est\nla plus difficile à détecter\nautomatiquement ?",
      "options": [
        {
          "text": "Les erreurs de syntaxe, car elles cassent toute l'exécution",
          "correct": false,
          "feedback": "Erreur : ces erreurs sont au\ncontraire les **plus faciles** à détecter. Python\nrefuse de lancer le programme\net affiche immédiatement\n`SyntaxError`, avec le\nnuméro de ligne. Aucune\nmystère.\n"
        },
        {
          "text": "Les erreurs d'exécution, car le programme s'arrête en cours de route",
          "correct": false,
          "feedback": "Erreur : ces erreurs sont\naussi facilement repérables.\nLe programme s'arrête en\nlevant une exception, avec\nune trace d'erreur précise\n(`IndexError`,\n`ZeroDivisionError`, etc.).\nOn voit immédiatement où\nchercher.\n"
        },
        {
          "text": "Les erreurs sémantiques, le programme tourne mais est faux",
          "correct": true,
          "feedback": "Bonne réponse : c'est le bug\nle plus pernicieux. Aucun\nmessage d'erreur, juste un\nrésultat faux. Seuls des\ntests bien choisis ou une\nrelecture attentive\npermettent de le détecter.\nExemple : une boucle qui\ncommence à l'indice 1 au\nlieu de 0.\n"
        },
        {
          "text": "Les trois catégories d'erreurs sont équivalentes",
          "correct": false,
          "feedback": "Erreur : ces trois\ncatégories diffèrent\nradicalement par leur mode\nde détection. La syntaxe est\ndétectée par l'analyseur\nsyntaxique, l'exécution par\nl'interpréteur, la sémantique\nuniquement par les tests ou\nla relecture humaine.\n"
        }
      ],
      "explanation": "Mémorable : « ce qui ne\ncompile pas, ne s'exécute\npas ; ce qui s'exécute peut\nplanter ; ce qui ne plante\npas peut quand même être\nfaux ». Les tests servent\nsurtout à détecter la\ntroisième catégorie."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "tests-multiples"
      ],
      "title": "Plusieurs cas dans un test",
      "partie": "tests",
      "statement": "Comment écrire un test qui\nvérifie le comportement d'une\nfonction `carre` sur **plusieurs entrées** sans dupliquer le code\n?",
      "options": [
        {
          "text": "```python\ndef test_carre():\n    return carre(0) and carre(1) and carre(3)\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code ne vérifie\npas que les valeurs\nrenvoyées sont\n**correctes**. Il vérifie\nseulement qu'aucune n'est\nfausse au sens booléen.\nComme `carre(0) == 0`,\nl'expression renvoie `0`\n(faux) sans que cela\nsignale un bug.\n"
        },
        {
          "text": "```python\ndef test_carre():\n    assert carre(0) == 0\n    assert carre(1) == 1\n    assert carre(3) == 9\n    assert carre(-2) == 4\n```\n",
          "correct": false,
          "feedback": "Cette version fonctionne\naussi, mais elle répète la\nmême logique de test à\nchaque ligne, ce que\nl'énoncé demandait\nprécisément d'éviter. La\nversion paramétrée factorise\nce schéma et indique, en cas\nd'échec, la valeur fautive.\n"
        },
        {
          "text": "```python\ndef test_carre():\n    for x, attendu in [(0, 0), (1, 1), (3, 9), (-2, 4)]:\n        assert carre(x) == attendu, \"échec pour x = \" + str(x)\n```",
          "correct": true,
          "feedback": "Bonne réponse : on parcourt une liste de couples\n`(entrée, sortie attendue)` et on vérifie chaque cas avec `assert`.\nLe message après la virgule indique précisément quel cas a échoué,\nce qui facilite le débogage.\n"
        },
        {
          "text": "```python\ndef test_carre():\n    for x, attendu in [(0, 0), (1, 1), (3, 9), (-2, 4)]:\n        assert carre(x), attendu\n```\n",
          "correct": false,
          "feedback": "Erreur : la virgule n'est pas une\ncomparaison. `assert carre(x), attendu`\nvérifie seulement que `carre(x)` est vrai\net utilise `attendu` comme message\nd'échec : le test échoue pour x = 0 (car 0\nest faux) et ne compare jamais la valeur\nrenvoyée à la valeur attendue.\n"
        }
      ],
      "explanation": "Schéma général : une liste de couples `(entrée, sortie attendue)`,\nparcourue dans une boucle qui vérifie chaque cas avec `assert`.\nAjouter un cas ne demande qu'un couple de plus dans la liste."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "assert",
        "flottant",
        "tolerance"
      ],
      "title": "Tester un résultat flottant",
      "partie": "tests",
      "statement": "Le test `assert moyenne([0.1, 0.2]) == 0.15` échoue alors que la\nfonction semble correcte. Comment écrire ce test correctement ?",
      "options": [
        {
          "text": "`assert abs(moyenne([0.1, 0.2]) - 0.15) < 1e-9`",
          "correct": true,
          "feedback": "Bonne réponse : les flottants ne sont pas exacts ; on vérifie\nque l'écart avec la valeur attendue est négligeable.\n"
        },
        {
          "text": "`assert moyenne([0.1, 0.2]) == 0.15000000000000002`",
          "correct": false,
          "feedback": "Erreur : recopier la valeur affichée rend le test fragile et\nincompréhensible.\n"
        },
        {
          "text": "`assert round(moyenne([0.1, 0.2])) == 0.15`",
          "correct": false,
          "feedback": "Erreur : `round` sans précision arrondit à l'entier, le test\néchouerait toujours.\n"
        },
        {
          "text": "En supprimant ce test, les flottants ne se testant pas",
          "correct": false,
          "feedback": "Erreur : les calculs flottants se testent, avec une tolérance.\n"
        }
      ],
      "explanation": "Comparer des flottants demande une tolérance : `abs(a - b) < eps`\nest le schéma à retenir."
    },
    {
      "id": "q29",
      "difficulty": 1,
      "skills": [
        "tests",
        "organisation",
        "fonction-de-test"
      ],
      "title": "Regrouper les tests d'une fonction",
      "partie": "tests",
      "statement": "Pourquoi écrire les assertions qui testent `moyenne` dans une\nfonction `test_moyenne()` plutôt qu'en vrac dans le programme ?",
      "options": [
        {
          "text": "Pour relancer tous les tests d'un seul appel",
          "correct": true,
          "feedback": "Bonne réponse : la fonction de test se rappelle après chaque\nchangement de code, ce qui détecte aussitôt une régression.\n"
        },
        {
          "text": "Parce que `assert` n'est autorisé que dans une fonction",
          "correct": false,
          "feedback": "Erreur : `assert` s'écrit n'importe où.\n"
        },
        {
          "text": "Pour que les tests s'exécutent plus vite, en bloc",
          "correct": false,
          "feedback": "Erreur : la vitesse est la même ; c'est l'organisation qui\nchange.\n"
        },
        {
          "text": "Pour cacher les tests aux utilisateurs du programme",
          "correct": false,
          "feedback": "Erreur : les tests ne sont pas secrets, ils documentent même\nl'usage attendu.\n"
        }
      ],
      "explanation": "Des tests regroupés et nommés forment une batterie réutilisable :\nun appel, un verdict."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "tests",
        "print",
        "return"
      ],
      "title": "Une fonction difficile à tester",
      "partie": "tests",
      "statement": "La fonction `calcul(a, b)` affiche le résultat avec `print` au lieu\nde le renvoyer. Pourquoi est-elle difficile à tester avec `assert` ?",
      "options": [
        {
          "text": "Parce qu'elle ne renvoie rien à comparer, l'affichage n'étant pas une valeur",
          "correct": true,
          "feedback": "Bonne réponse : `assert calcul(2, 3) == 5` compare `None` à\n`5`. Renvoyer le résultat, et l'afficher ailleurs, rend la\nfonction testable.\n"
        },
        {
          "text": "Parce que `print` est plus lent qu'un `return`",
          "correct": false,
          "feedback": "Erreur : la vitesse n'est pas en cause.\n"
        },
        {
          "text": "Parce qu'une fonction avec `print` ne peut pas être appelée dans un test",
          "correct": false,
          "feedback": "Erreur : elle peut être appelée, mais son résultat n'est pas\nrécupérable.\n"
        },
        {
          "text": "Parce que les tests n'acceptent que des fonctions à un paramètre",
          "correct": false,
          "feedback": "Erreur : le nombre de paramètres n'a aucune importance.\n"
        }
      ],
      "explanation": "Séparer le calcul de l'affichage est une règle de conception :\nles fonctions qui renvoient se testent, celles qui affichent non."
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "tests",
        "nombre",
        "specification"
      ],
      "title": "Combien de tests écrire",
      "partie": "tests",
      "statement": "La spécification de `tarif(age)` distingue trois cas : moins de\n$12$ ans, de $12$ à $17$ ans, $18$ ans et plus. Combien de tests\nécrire au minimum ?",
      "options": [
        {
          "text": "Au moins un par cas, plus un à chaque frontière",
          "correct": true,
          "feedback": "Bonne réponse : trois cas appellent trois tests, et les valeurs\n$12$ et $18$ méritent chacune un test supplémentaire.\n"
        },
        {
          "text": "Un seul, avec une valeur quelconque",
          "correct": false,
          "feedback": "Erreur : un seul test laisse deux cas sans vérification.\n"
        },
        {
          "text": "Cent, pour être sûr de couvrir tous les âges",
          "correct": false,
          "feedback": "Erreur : le nombre ne fait pas la qualité ; ce sont les cas\ncouverts qui comptent.\n"
        },
        {
          "text": "Aucun, la spécification suffisant à garantir le code",
          "correct": false,
          "feedback": "Erreur : la spécification dit ce qui doit se passer ; les tests\nvérifient que le code le fait.\n"
        }
      ],
      "explanation": "Le jeu de tests se déduit de la spécification : chaque cas, chaque\nfrontière."
    },
    {
      "id": "q32",
      "difficulty": 3,
      "skills": [
        "tests",
        "independance",
        "ordre"
      ],
      "title": "Des tests qui dépendent les uns des autres",
      "partie": "tests",
      "statement": "Le test `test_retrait` ne réussit que s'il est lancé après\n`test_depot`, qui alimente le compte. Quel est le problème ?",
      "options": [
        {
          "text": "Les tests ne sont pas indépendants",
          "correct": true,
          "feedback": "Bonne réponse : un test doit pouvoir être lancé seul, dans\nn'importe quel ordre. Sinon un échec devient impossible à\ninterpréter.\n"
        },
        {
          "text": "Aucun, l'ordre des tests étant toujours le même",
          "correct": false,
          "feedback": "Erreur : l'ordre peut changer, et un test qui dépend d'un autre\ncache une mauvaise conception.\n"
        },
        {
          "text": "`test_depot` devrait être supprimé",
          "correct": false,
          "feedback": "Erreur : les deux tests sont utiles ; c'est leur dépendance qui\npose problème.\n"
        },
        {
          "text": "Les deux tests devraient être fusionnés en un seul",
          "correct": false,
          "feedback": "Erreur : fusionner masque quelle opération échoue.\n"
        }
      ],
      "explanation": "Chaque test construit son contexte, exécute une opération, vérifie\nle résultat : indépendant, il reste lisible quand il échoue."
    },
    {
      "id": "q33",
      "difficulty": 3,
      "skills": [
        "tests",
        "test-faible"
      ],
      "title": "Un test qui ne teste rien",
      "partie": "tests",
      "statement": "Que vaut le test `assert double(4) == double(4)` ?",
      "options": [
        {
          "text": "Rien, il réussit quel que soit le résultat de `double`",
          "correct": true,
          "feedback": "Bonne réponse : comparer la fonction à elle-même ne dit pas si\nelle renvoie $8$. Un test compare le résultat à une valeur\nattendue, écrite à la main.\n"
        },
        {
          "text": "Il vérifie que `double` est déterministe",
          "correct": false,
          "feedback": "Erreur : même si c'était l'intention, cela ne vérifie pas la\nspécification de `double`.\n"
        },
        {
          "text": "Il vérifie que `double(4)` vaut $8$",
          "correct": false,
          "feedback": "Erreur : le nombre $8$ n'apparaît nulle part.\n"
        },
        {
          "text": "Il échoue toujours, une fonction ne pouvant être comparée à elle-même",
          "correct": false,
          "feedback": "Erreur : deux appels identiques donnent la même valeur, le test\npasse.\n"
        }
      ],
      "explanation": "La valeur attendue d'un test vient de la spécification, jamais du\ncode testé."
    },
    {
      "id": "q34",
      "difficulty": 1,
      "skills": [
        "trace-erreur",
        "indexerror"
      ],
      "title": "Un indice hors de la liste",
      "partie": "debogage",
      "statement": "Que signifie le message `IndexError: list index out of range` ?",
      "options": [
        {
          "text": "Un indice inexistant dans la liste a été utilisé",
          "correct": true,
          "feedback": "Bonne réponse : avec une liste de cinq éléments, `t[5]`\nprovoque cette erreur ; le dernier indice valide est $4$.\n"
        },
        {
          "text": "La liste est trop longue pour la mémoire disponible",
          "correct": false,
          "feedback": "Erreur : la taille de la liste n'est pas en cause.\n"
        },
        {
          "text": "La liste contient un élément du mauvais type",
          "correct": false,
          "feedback": "Erreur : le type des éléments donnerait une `TypeError`.\n"
        },
        {
          "text": "La liste n'a pas été définie avant son utilisation",
          "correct": false,
          "feedback": "Erreur : une liste non définie donnerait une `NameError`.\n"
        }
      ],
      "explanation": "Chaque type d'erreur nomme sa cause : `IndexError` pour un indice,\n`KeyError` pour une clé, `NameError` pour un nom inconnu."
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "trace-erreur",
        "typeerror"
      ],
      "title": "Une concaténation refusée",
      "partie": "debogage",
      "statement": "Le message `TypeError: can only concatenate str (not \"int\") to str`\napparaît. Quelle instruction peut l'avoir provoqué ?",
      "options": [
        {
          "text": "`\"Total : \" + 12`",
          "correct": true,
          "feedback": "Bonne réponse : `+` entre une chaîne et un entier est refusé.\nIl faut `str(12)` ou une virgule dans `print`.\n"
        },
        {
          "text": "`\"Total : \" + \"12\"`",
          "correct": false,
          "feedback": "Erreur : deux chaînes se concatènent sans problème.\n"
        },
        {
          "text": "`12 + 3`",
          "correct": false,
          "feedback": "Erreur : deux entiers s'additionnent.\n"
        },
        {
          "text": "`int(\"12\")`",
          "correct": false,
          "feedback": "Erreur : cette conversion est valide.\n"
        }
      ],
      "explanation": "Le message indique les deux types en conflit : c'est lui qui\ndésigne la conversion à ajouter."
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "debogage",
        "reproduction",
        "exemple-minimal"
      ],
      "title": "Réduire le cas qui échoue",
      "partie": "debogage",
      "statement": "La fonction `trier` donne un résultat faux sur une liste de deux\nmille éléments. Quelle est la première chose à faire ?",
      "options": [
        {
          "text": "Chercher la plus petite liste qui reproduit l'erreur",
          "correct": true,
          "feedback": "Bonne réponse : une liste de trois ou quatre éléments qui échoue\nse trace à la main et révèle le mécanisme du bug.\n"
        },
        {
          "text": "Relancer le programme jusqu'à ce que le résultat soit bon",
          "correct": false,
          "feedback": "Erreur : un bug déterministe se reproduit à chaque fois.\n"
        },
        {
          "text": "Réécrire la fonction entièrement",
          "correct": false,
          "feedback": "Erreur : sans comprendre l'erreur, on risque de la réécrire.\n"
        },
        {
          "text": "Ajouter des éléments à la liste pour voir si l'erreur persiste",
          "correct": false,
          "feedback": "Erreur : agrandir le cas le rend encore plus difficile à\nanalyser.\n"
        }
      ],
      "explanation": "Réduire l'exemple est la première étape du débogage : plus le cas\nest petit, plus la cause est visible."
    },
    {
      "id": "q37",
      "difficulty": 3,
      "skills": [
        "debogage",
        "methode",
        "modification-unique"
      ],
      "title": "Une modification à la fois",
      "partie": "debogage",
      "statement": "Pour corriger un bug, Théo change trois lignes d'un coup, et le\nrésultat devient juste. Pourquoi cette méthode est-elle risquée ?",
      "options": [
        {
          "text": "Il ignore laquelle a corrigé le bug, et si les autres en créent un",
          "correct": true,
          "feedback": "Bonne réponse : modifier une seule chose, tester, puis\ncontinuer permet d'attribuer chaque effet à sa cause.\n"
        },
        {
          "text": "Trois modifications prennent trop de temps à écrire",
          "correct": false,
          "feedback": "Erreur : le temps d'écriture n'est pas le problème.\n"
        },
        {
          "text": "Python n'accepte qu'une modification par exécution",
          "correct": false,
          "feedback": "Erreur : rien de tel n'existe.\n"
        },
        {
          "text": "Le résultat juste prouve que les trois lignes étaient fausses",
          "correct": false,
          "feedback": "Erreur : une seule ligne fautive suffit à expliquer le bug.\n"
        }
      ],
      "explanation": "Le débogage est une expérience scientifique : une hypothèse, une\nmodification, une vérification."
    },
    {
      "id": "q38",
      "difficulty": 3,
      "skills": [
        "trace-erreur",
        "syntaxe",
        "ligne"
      ],
      "title": "Une erreur signalée à la mauvaise ligne",
      "partie": "debogage",
      "statement": "Python signale une `SyntaxError` à la ligne $12$, qui semble\npourtant correcte. Que vérifier ?",
      "options": [
        {
          "text": "La ligne précédente, une parenthèse non fermée y étant fréquente",
          "correct": true,
          "feedback": "Bonne réponse : une parenthèse ouverte à la ligne $11$ fait lire\nla ligne $12$ comme la suite de l'expression, et l'erreur n'est\ndétectée que là.\n"
        },
        {
          "text": "La version de Python, qui change la numérotation des lignes",
          "correct": false,
          "feedback": "Erreur : les numéros de ligne sont fiables ; c'est la cause qui\nest en amont.\n"
        },
        {
          "text": "La ligne $13$, Python se trompant toujours d'une ligne",
          "correct": false,
          "feedback": "Erreur : Python ne se trompe pas ; il signale où la lecture\néchoue.\n"
        },
        {
          "text": "Rien, une `SyntaxError` disparaissant au second lancement",
          "correct": false,
          "feedback": "Erreur : une erreur de syntaxe est stable tant que le code n'est\npas corrigé.\n"
        }
      ],
      "explanation": "Quand la ligne signalée paraît juste, remonter d'une ligne : une\nparenthèse, un crochet ou des guillemets non fermés en sont presque\ntoujours la cause."
    },
    {
      "id": "q39",
      "difficulty": 1,
      "skills": [
        "erreur-syntaxe",
        "erreur-execution"
      ],
      "title": "Avant ou pendant l'exécution",
      "partie": "debogage",
      "statement": "Quelle différence entre une `SyntaxError` et une `ZeroDivisionError` ?",
      "options": [
        {
          "text": "La première est détectée avant l'exécution, la seconde pendant",
          "correct": true,
          "feedback": "Bonne réponse : une faute de syntaxe empêche le programme de\ndémarrer ; une division par zéro n'apparaît que lorsque\nl'instruction fautive est atteinte.\n"
        },
        {
          "text": "La première est grave, la seconde sans conséquence sur le résultat",
          "correct": false,
          "feedback": "Erreur : les deux arrêtent le programme.\n"
        },
        {
          "text": "La première vient de Python, la seconde du système d'exploitation",
          "correct": false,
          "feedback": "Erreur : les deux sont signalées par Python.\n"
        },
        {
          "text": "Aucune, ce sont deux noms pour la même erreur d'exécution",
          "correct": false,
          "feedback": "Erreur : elles ont des causes et des moments très différents.\n"
        }
      ],
      "explanation": "Erreurs de syntaxe au chargement, erreurs d'exécution en cours de\nroute, erreurs de logique sans message : trois familles, trois\nfaçons de les chercher."
    }
  ]
}