Gestion des erreurs en R : déboguer, tester, valider
Faites échouer R bruyamment, déboguez vite, validez vos résultats et vérifiez le code écrit par une IA
Un script qui s’exécute sans erreur peut tout de même être faux — et désormais un chatbot écrit la moitié de votre code. Cette leçon parle de confiance : faire échouer R bruyamment et correctement (stop/warning/stopifnot/tryCatch), déboguer vite quand ça arrive (traceback/browser/recover), valider vos données et vos résultats (assertions, pointblank, testthat), et vérifier que le code écrit par une IA s’exécute réellement et est correct.
Date de publication
19 juillet 2026
Modifié
19 juillet 2026
AstucePoints clés
Échouez bruyamment, pas silencieusement. Un script qui se termine sans se plaindre peut tout de même être faux. Utilisez stop() pour interrompre en cas d’entrée invalide, warning() pour quelque chose de suspect, et stopifnot() pour des assertions en une ligne — un échec bruyant que vous voyez vaut mieux qu’un nombre discret auquel vous ne pouvez pas vous fier.
Attrapez à dessein, pas partout.tryCatch() gère un échec attendu et renvoie une valeur de repli ; withCallingHandlers() journalise et continue. Tout envelopper dans tryCatch(..., error = function(e) NULL) ne fait que masquer les bogues — laissez planter les erreurs inattendues.
Le débogage est le chemin rapide, pas le but.traceback() montre où ça a cassé, browser() met en pause pour que vous puissiez regarder autour, options(error = recover) vous dépose dans l’appel qui a échoué. Un copilote écrit le code ; c’est vous qui le diagnostiquez quand il est faux.
Validez les données et les résultats. Affirmez ce que vos entrées doivent respecter (pointblank), et testez vos fonctions d’analyse (testthat) — pas seulement les packages. Un résultat que vous ne pouvez pas vérifier est un résultat auquel vous ne pouvez pas vous fier.
Vérifiez le code écrit par une IA. Exécutez-le sur une entrée connue, affirmez la sortie attendue, puis soumettez-le à des cas limites. Ce contrôle — le runtime comme juge — est une compétence qu’aucun chatbot ne peut faire à votre place.
Introduction
Deux échecs vous coûtent bien plus cher qu’une trace d’appels ne le fera jamais. Le premier : un script qui s’exécute proprement, affiche un nombre plausible et est discrètement faux — un NA silencieux, une jointure qui a supprimé la moitié de vos lignes, une confusion d’unités. Le second, plus récent : une fonction qu’un assistant a écrite, qui paraît correcte, a passé votre coup d’œil, et casse sur la seule entrée que vous n’avez pas essayée.
Ni l’un ni l’autre n’est un problème de syntaxe. Les deux sont des problèmes de confiance, et cette leçon a pour but de regagner cette confiance — faire en sorte que R vous dise quand quelque chose ne va pas, et vous donner un moyen rapide de vérifier qu’un résultat (le vôtre ou celui d’une IA) est réellement correct.
Un peu de vocabulaire d’abord, car le système d’erreurs de R a des termes précis. Une condition est un signal que R émet pendant l’exécution ; les trois que vous utiliserez sont une erreur (arrête l’exécution — quelque chose ne va pas et ne peut pas continuer), un avertissement (l’exécution continue, mais quelque chose semblait anormal) et un message (simple sortie de diagnostic, pas un problème). Une assertion est une vérification qu’un fait énoncé est vrai, qui échoue bruyamment si ce n’est pas le cas. Un test unitaire est une petite vérification automatique qu’une fonction renvoie ce que vous attendez sur une entrée connue. Nous utiliserons tout cela pour construire du code sur lequel vous pouvez compter.
Voici la progression : échouer bruyamment pour que les problèmes fassent surface → déboguer efficacement quand ils surviennent → valider vos données et vos résultats pour que « ça a tourné » devienne « c’est juste » → vérifier le code écrit par une IA de la même manière. C’est exactement le jugement qu’un copilote ne peut pas vous donner : il écrit volontiers la fonction ; décider s’il faut lui faire confiance reste votre travail.
NoteComment les exemples s’exécutent
Chaque exemple est autonome — copiez n’importe quel bloc et il s’exécute tout seul. Les outils de débogage du milieu (browser(), debug(), recover()) sont interactifs : ils mettent en pause une session en cours, ils sont donc montrés sous forme d’extraits à exécuter dans votre propre console R, pas exécutés ici. Tout le reste de cette page est une sortie réelle, réellement exécutée.
Échouer bruyamment et correctement
La pire erreur est celle qui ne se produit pas. Si une fonction accepte n’importe quoi et renvoie tout de même un nombre, le bogue voyage en aval et fait surface sous forme de conclusion erronée, pas de message rouge. Le premier travail est donc de faire s’arrêter les mauvaises entrées — à la porte, avec un message qui nomme le problème.
stop(), warning(), message() — dire ce qui ne va pas
Trois signaux, trois niveaux de gravité. stop()interrompt avec une erreur (l’appelant doit la corriger), warning() laisse l’exécution continuer mais signale quelque chose de suspect, et message() affiche un simple diagnostic. Vérifiez d’emblée les hypothèses dont dépend votre fonction, puis faites-leur confiance :
celsius_to_f <-function(x) {if (!is.numeric(x)) {stop("`x` must be numeric, not ", class(x)[1]) # abort: can't continue }if (any(x <-273.15, na.rm =TRUE)) {warning("Some values are below absolute zero — check your data.") # continue, but flag } x *9/5+32}# A good error message names the problem AND the offending value:tryCatch(celsius_to_f("warm"), error =function(e) conditionMessage(e))
[1] "`x` must be numeric, not character"
Le message nomme à la fois l’attente (numeric) et ce qu’il a reçu (character), de sorte que l’appelant le corrige sans ouvrir votre fonction. C’est tout l’intérêt d’une bonne erreur : elle transforme « quelque chose a cassé » en « cette chose précise a cassé, ici ».
stopifnot() — des assertions en une ligne
Écrire un if (...) stop(...) pour chaque précondition devient verbeux. stopifnot() affirme plusieurs faits à la fois et interrompt au premier qui est faux — et si vous nommez la condition, ce nom devient le message d’erreur :
total_revenue <-function(df) {stopifnot(is.data.frame(df),"df must have rows"=nrow(df) >0,"price must be positive"=all(df$price >0) )sum(df$price)}# A bad frame fails with the named message, not a cryptic downstream error:tryCatch(total_revenue(data.frame(price =c(19.99, -5.00))),error =function(e) conditionMessage(e))
[1] "price must be positive"
# A valid frame passes the gate and computes:total_revenue(data.frame(price =c(19.99, 5.00, 42.50)))
[1] 67.49
Des assertions comme celles-ci sont une assurance bon marché. Elles documentent ce que la fonction suppose et l’imposent, de sorte qu’une violation apparaît ici — à la cause — au lieu de trois fonctions plus loin sous la forme d’un mystérieux NA.
tryCatch() — gérer un échec attendu
Parfois un échec est attendu et vous voulez vous en remettre : un fichier peut être absent, une valeur peut ne pas s’analyser. tryCatch() exécute une expression et achemine chaque type de condition vers un gestionnaire. Ici une fonction utilitaire renvoie NA (et journalise quelle condition elle a rencontrée) au lieu de faire planter tout le script :
safe_log <-function(x) {tryCatch(log(x),warning =function(w) { message("warning: ", conditionMessage(w)); NA_real_ },error =function(e) { message("error: ", conditionMessage(e)); NA_real_ },finally =message("done checking x = ", deparse(x)) )}safe_log(100) # normal case
[1] 4.60517
safe_log(-1) # log(-1) warns (NaN) -> handled, returns NA
[1] NA
safe_log("x") # log("x") errors -> handled, returns NA
[1] NA
Chaque appel est acheminé : l’entrée valide renvoie sa valeur, l’avertissement et l’erreur sont chacun attrapés et transformés en un NA propre, et finally s’exécute toujours (utile pour fermer une connexion ou une barre de progression). tryCatch est fait pour les échecs que vous avez anticipés et auxquels vous avez une réponse sensée.
withCallingHandlers() — journaliser, puis continuer
tryCatch()déroule la pile : une fois qu’un gestionnaire s’exécute, l’expression est terminée. Mais parfois vous voulez noter un avertissement et laisser le calcul continuer là où il en était — journaliser sans interrompre. C’est withCallingHandlers() plus un redémarrage :
L’avertissement sur la première ligne est journalisé, étouffé, et la boucle continue de calculer chaque ligne — vous obtenez à la fois les diagnostics et le résultat complet. Utilisez withCallingHandlers() quand un avertissement est informatif mais pas fatal ; utilisez tryCatch() quand vous voulez vous arrêter et renvoyer une valeur de repli.
NoteL’attraper, ou la laisser planter ?
L’instinct de tout envelopper dans tryCatch(..., error = function(e) NULL) est un piège — il convertit des bogues bruyants en NULL silencieux (voir Problèmes fréquents). Décidez délibérément :
flowchart TD
A[Quelque chose peut mal tourner] --> B{Pouvez-vous raisonnablement<br/>récupérer ici ?}
B -->|Non| C[Laissez planter —<br/>échec bruyant, corrigez la cause]
B -->|Oui| D{Cas attendu<br/>et gérable ?}
D -->|Oui| E[tryCatch —<br/>gérez-le, renvoyez une valeur de repli]
D -->|Non| F[withCallingHandlers —<br/>journalisez et continuez]
Des erreurs typées avec rlang::abort()
Quand un appelant doit réagir différemment à des échecs différents, donnez une classe à vos erreurs. rlang::abort() émet une condition typée, de sorte qu’un gestionnaire peut attraper cette classe précise plutôt que de correspondre au texte du message (ce qui est fragile et casse à la traduction) :
library(rlang)read_positive <-function(x) {if (!is.numeric(x)) abort("x must be numeric", class ="invalid_type")if (any(x <0)) abort("x must be non-negative", class ="negative_value")sqrt(x)}# The caller catches by CLASS, not by message string:tryCatch(read_positive(-4),negative_value =function(e) paste("handled a negative value:", conditionMessage(e)))
[1] "handled a negative value: x must be non-negative"
Attraper negative_value gère exactement ce cas et laisse toutes les autres erreurs se propager. Pour tout ce qui dépasse un script — un package, un pipeline partagé — les conditions typées sont la façon dont les appelants réagissent par programmation au lieu de faire du grep sur le texte des erreurs. Le chapitre « Conditions » d’Advanced R est la référence pour le système de conditions complet de R.
Déboguer efficacement quand ça casse
Un bon signalement fait que les échecs s’annoncent. Maintenant vous devez trouver la cause — vite. Voici les outils ; considérez-les comme le chemin rapide vers un correctif, pas comme la destination. (Un copilote écrira volontiers le code ; quand il se comporte mal, voici comment vous voyez pourquoi.)
traceback() — où est-ce que ça a cassé ?
Un traceback est la séquence d’appels de fonctions qui a mené à l’erreur — la pile d’appels. Après n’importe quelle erreur, traceback() l’affiche, l’appel le plus récent en premier, pour que vous voyiez le chemin exact :
outer <-function(x) inner(x)inner <-function(x) sqrt(x)outer("a")#> Error in sqrt(x) : non-numeric argument to mathematical functiontraceback()#> 2: inner(x) at #1#> 1: outer("a")
La pile vous dit que l’erreur s’est déclenchée dans inner(), appelé depuis outer() — vous savez où regarder avant de lire une seule ligne du corps. Dans RStudio, la même pile apparaît dans le panneau Traceback, et vous pouvez cliquer sur un cadre pour y accéder.
Inspecter l’objet condition
Vous n’avez pas à deviner ce qu’est une erreur — attrapez-la et regardez. Une erreur n’est qu’un objet avec un message et un ensemble de classes :
e <-tryCatch(sqrt("x"), error =function(e) e)conditionMessage(e) # the human-readable message
[1] "non-numeric argument to mathematical function"
class(e) # its condition classes (match the innermost first)
[1] "simpleError" "error" "condition"
conditionMessage() vous donne le texte ; class() montre les classes qu’un gestionnaire tryCatch pourrait attraper. C’est ainsi que vous décidez quel nom de gestionnaire écrire — et que vous confirmez qu’une erreur typée (issue d’abort()) porte bien la classe que vous attendez.
browser(), debug() et recover() — entrer à l’intérieur
L’outil de débogage le plus efficace de tous consiste à mettre l’exécution en pause et regarder autour. Déposez un browser() dans une fonction et R s’arrête là, vous remettant une invite interactive avec les variables locales de la fonction en direct — inspectez-les, avancez d’un pas avec n, exécutez jusqu’à la fin avec c, quittez avec Q :
row_share <-function(df) { total <-sum(df$value)browser() # pauses here — inspect df, total, then step with n / c / Q df$value / total}
Deux proches font la même chose sans modifier la fonction. debug(f) parcourt pas à pas chaque appel à f() jusqu’à ce que vous fassiez undebug(f) ; debugonce(f) le fait pour le prochain appel seulement :
debugonce(row_share) # step through just the next callrow_share(my_data)
Et pour attraper n’importe quelle erreur de façon interactive, réglez options(error = recover) — après un plantage, R liste la pile d’appels et vous laisse descendre dans le cadre qui a échoué pour inspecter ses variables :
options(error = recover) # after any error, choose a frame to inspect# ... run the code that fails ...options(error =NULL) # restore the default when done
Pour un bogue tenace du type « pourquoi cette valeur est-elle fausse », un browser() sur la ligne suspecte — puis l’affichage de la variable fautive avec str() pour voir sa structure et son type — résout la plupart des cas en une minute. (Le chapitre « Debugging » d’Advanced R est la référence approfondie.)
Valider vos données et vos résultats
Voici le cœur du sujet. La gestion des erreurs et le débogage traitent du code qui casse. Le cas dangereux est le code qui tourne et qui est faux — et la seule défense est de vérifier ce qui doit être vrai : vos entrées avant de calculer, et vos sorties (via les fonctions qui les produisent) après.
Affirmer ce qui doit être vrai de vos données
Avant qu’une analyse ne touche un jeu de données, affirmez sa forme. La version la moins coûteuse est le stopifnot() vu plus haut ; pour un vrai jeu de données avec plusieurs règles au niveau des colonnes, pointblank est conçu pour ça. Vous créez un agent sur la table, déclarez des attentes colonne par colonne, et interrogate() les exécute toutes :
library(pointblank)set.seed(1)orders <-data.frame(id =1:6,region =c("North", "South", "East", "West", "North", "South"),units =c(3, 5, 2, 8, 4, 1),price =c(19.99, 5.00, 42.50, 12.00, 8.75, 99.00))agent <-create_agent(tbl = orders, tbl_name ="orders") |>col_vals_not_null(vars(region)) |># region is never missingcol_is_numeric(vars(units, price)) |># numeric columns really are numericcol_vals_gt(vars(price), value =0) |># every price is positivecol_vals_between(vars(units), left =1, right =20) |># units in a sane rangeinterrogate()# A compact pass/fail report (f_pass = fraction passing):get_agent_report(agent, display_table =FALSE)[ , c("i", "type", "columns", "values", "units", "n_pass", "f_pass")]
# A tibble: 5 × 7
i type columns values units n_pass f_pass
<int> <chr> <chr> <chr> <dbl> <dbl> <dbl>
1 1 col_vals_not_null region <NA> 6 6 1
2 2 col_is_numeric units <NA> 1 1 1
3 3 col_is_numeric price <NA> 1 1 1
4 4 col_vals_gt price 0 6 6 1
5 5 col_vals_between units 1, 20 6 6 1
all_passed(agent)
[1] TRUE
Chaque règle passe (f_pass = 1, all_passed() vaut TRUE), donc les données gagnent leur place dans l’analyse. Maintenant regardez ce qui se passe quand ce n’est pas le cas — un prix corrompu et une région manquante :
# A tibble: 2 × 5
i type columns n_pass f_pass
<int> <chr> <chr> <dbl> <dbl>
1 1 col_vals_not_null region 5 0.833
2 2 col_vals_gt price 5 0.833
all_passed(agent)
[1] FALSE
all_passed() vaut maintenant FALSE, et le rapport pointe quelle règle a échoué et de combien (5 lignes sur 6 passent chaque contrôle). Dans un pipeline, vous vous embrancheriez sur all_passed() — arrêter, alerter, mettre en quarantaine les mauvaises lignes — au lieu de moyenner silencieusement un prix négatif dans votre chiffre d’affaires. (Dans une session interactive, l’affichage de l’agent produit le rapport couleur complet de pointblank ; le tableau compact ci-dessus est la version qui tient sur une page statique.)
Tester vos fonctions d’analyse
Les packages sont testés ; vos fonctions d’analyse, elles, ne le sont généralement pas — et ce sont elles qui calculent vos résultats. La solution est un test unitaire : une petite vérification qu’une fonction renvoie la valeur attendue sur une entrée connue. testthat fournit les verbes d’attente. Chaque expect_*() est silencieux quand il passe et échoue bruyamment quand il échoue — donc un bloc qui s’exécute jusqu’au bout sans sortie a vérifié tout ce qu’il contient :
library(testthat)# The analysis function we want to trust:discount <-function(price, pct) {if (pct <0|| pct >1) stop("`pct` must be a fraction in [0, 1]") price * (1- pct)}expect_equal(discount(100, 0.20), 80) # a known resultexpect_equal(discount(50, 0), 50) # a boundaryexpect_error(discount(100, 1.5), "must be a fraction") # bad input DOES abort
Rien d’affiché — les trois attentes ont tenu : le calcul est juste à un point connu et à la borne, et le garde-fou d’entrée se déclenche bien. Dans un package ou un projet, vous grouperiez les attentes liées dans un bloc test_that() et lanceriez toute la suite d’un coup (Run Tests de RStudio, ou testthat::test_file()) :
test_that("discount computes and validates its inputs", {expect_equal(discount(100, 0.20), 80)expect_equal(discount(50, 0), 50)expect_error(discount(100, 1.5), "must be a fraction")})
Le but n’est pas le cérémonial — c’est qu’un résultat que personne n’a vérifié est un résultat auquel personne ne peut se fier. Une poignée d’attentes sur les fonctions qui produisent vos nombres attrape la régression avant qu’elle n’atteigne une figure de votre article.
Vérifier le code écrit par une IA
C’est la boucle moderne, et c’est là que tout ce qui précède porte ses fruits. Un assistant rédige une fonction en quelques secondes ; elle se lit de façon plausible ; vous êtes sur le point de la coller. Ne le faites pas — vérifiez-la d’abord, exactement comme vous vérifieriez la vôtre : exécutez-la sur une entrée connue, affirmez la sortie attendue, puis poussez-la sur des cas limites. Le runtime, pas la prose, est le juge.
Disons que vous avez demandé un normaliseur min-max et reçu ceci. Ça a l’air correct, comme dans un manuel :
normalize <-function(x) { (x -min(x)) / (max(x) -min(x))}# Step 1 — run it on an input you KNOW the answer to:normalize(c(1, 2, 3)) # you expect 0.0, 0.5, 1.0
[1] 0.0 0.5 1.0
La bonne réponse sur le cas nominal — ce qui est exactement la façon dont un bogue subtil survit à la relecture. Donc étape 2 : épinglez cette attente comme un test, puis attaquez les bords qu’une IA oublie régulièrement — un vecteur constant (max - min devient-il zéro ?) et une valeur manquante :
Les voilà. Sur un vecteur constant, ça divise par zéro et renvoie NaN ; un seul NA empoisonne tout le résultat. Les deux s’exécutent sans erreur — pas d’exception, pas d’avertissement — ce qui est précisément pourquoi l’exécuter ne suffit pas et vérifier la sortie, oui. Maintenant corrigez-le et verrouillez le comportement avec des tests qui auraient attrapé les deux :
library(testthat)normalize <-function(x) { rng <-range(x, na.rm =TRUE) # ignore NA when finding the rangeif (diff(rng) ==0) return(rep(0, length(x))) # constant vector -> all zeros (x - rng[1]) /diff(rng)}# The full contract, asserted — normal, constant, and NA input:expect_equal(normalize(c(1, 2, 3)), c(0, 0.5, 1))expect_equal(normalize(c(5, 5, 5)), c(0, 0, 0))expect_equal(normalize(c(1, NA, 3)), c(0, NA, 1))
De nouveau silencieux — la fonction corrigée honore les trois cas. Voilà la boucle : générer → exécuter sur une entrée connue → affirmer → la casser sur les bords → corriger → garder les tests. Un chatbot peut écrire le premier jet, mais seul le runtime peut vous dire si le jet est correct, et vous seul pouvez décider quels bords comptent. Ce jugement ne se banalise pas — c’est toute la raison d’avoir un runtime sous vos réponses au lieu de simple prose.
🟢 Avec un agent IA
Vous avez une fonction qu’un assistant vient d’écrire et vous n’êtes pas sûr d’elle ? Collez-la et demandez à Prova« écris des tests qui vérifient ceci sur une entrée connue et ses cas limites ». Comme le runtime est juste là, vous exécutez ces tests immédiatement et voyez si la fonction tient — le contrôle qu’un chatbot sans runtime ne peut structurellement pas faire. Demander à Prova →
Problèmes fréquents
Une validation qui devrait interrompre se contente d’avertir. Si une mauvaise ligne peut corrompre votre résultat, warning() est trop doux — l’exécution continue et le déchet s’écoule en aval. Réservez warning() aux anomalies réellement récupérables ; utilisez stop() / stopifnot() (ou embranchez sur le all_passed() de pointblank) pour tout ce qui ne doit pas se poursuivre. Le test : si ceci est vrai, la réponse est-elle encore digne de confiance ? Si non, interrompez.
tryCatch() qui avale trop.tryCatch(risky(), error = function(e) NULL) transforme chaque échec — y compris le bogue que vous n’aviez pas anticipé — en un NULL silencieux, et vous passez une heure à traquer un NULL sans trace d’appels. Attrapez des conditions précises (par classe, via rlang::abort()), renvoyez une valeur de repli significative, et laissez planter les erreurs inattendues pour pouvoir les voir. Les captures larges cachent les bogues ; elles ne les corrigent pas.
Un test qui passe à vide.expect_true(length(result) >= 0) est toujours vrai — il ne teste rien. Il en va de même pour l’affirmation d’une valeur que vous avez calculée avec la fonction même que vous testez. Un vrai test nomme la réponse indépendamment (expect_equal(discount(100, 0.2), 80) — le 80 vient d’une arithmétique faite à la main, pas de discount()), et inclut au moins une entrée conçue pour échouer si la logique casse.
Faire confiance à une fonction d’IA parce qu’elle a tourné. « Elle s’est exécutée et a renvoyé un nombre » n’est pas « elle est correcte » — le normaliseur ci-dessus faisait les deux et était tout de même faux sur un vecteur constant. Exécutez toujours le code généré sur une entrée dont vous connaissez la réponse, affirmez-la, et essayez les bords (vecteur vide, NA, une valeur limite, un seul élément). Du code qui s’exécute proprement peut tout de même être discrètement, et coûteusement, faux.
Questions fréquentes
NoteComment gérer les erreurs en R ?
Signalez les problèmes avec stop() (interrompre), warning() (continuer mais signaler) ou message() (simple diagnostic), et vérifiez les préconditions avec stopifnot(). Pour vous remettre d’un échec attendu, enveloppez l’expression risquée dans tryCatch() et donnez-lui un gestionnaire error = (et/ou warning =) qui renvoie une valeur de repli sensée. Utilisez withCallingHandlers() quand vous voulez journaliser un avertissement et poursuivre le calcul plutôt que de le dérouler.
NoteQuelle est la différence entre try() et tryCatch() en R ?
try() exécute une expression et, en cas d’échec, renvoie un objet spécial try-error au lieu d’arrêter le script — vous vérifiez ensuite le résultat avec inherits(x, "try-error"). tryCatch() est plus puissant : il vous laisse enregistrer des gestionnaires distincts pour les erreurs, les avertissements, les messages, et un bloc finally, et acheminer chacun vers du code différent. Utilisez try() pour un rapide « ne me laisse pas m’arrêter là-dessus » ; utilisez tryCatch() quand vous voulez gérer des conditions précises délibérément.
NoteComment déboguer du code R ?
Après une erreur, appelez traceback() pour voir la pile d’appels qui y a mené. Pour entrer dans une fonction, insérez browser() sur la ligne suspecte (ou utilisez debug(f) / debugonce(f)) et inspectez les variables locales avec print() et str(). Pour attraper n’importe quelle erreur de façon interactive, réglez options(error = recover), qui vous dépose dans l’appel défaillant pour que vous puissiez regarder ses variables. Ceux-ci mettent en pause une session en cours, exécutez-les donc dans votre propre console R.
NoteComment écrire des tests unitaires en R ?
Utilisez le package testthat. Chaque attente — expect_equal(actual, expected), expect_error(code, "pattern"), expect_true(cond) — est silencieuse quand elle passe et échoue quand elle échoue. Groupez les attentes liées dans un bloc test_that("description", { ... }), enregistrez-les sous tests/testthat/, et lancez la suite avec testthat::test_file() ou Run Tests de RStudio. Testez les fonctions d’analyse qui calculent vos résultats, pas seulement les packages — un bon test nomme la réponse attendue indépendamment et inclut une entrée conçue pour échouer si la logique casse.
NoteComment vérifier du code que ChatGPT (ou n’importe quelle IA) a écrit en R ?
Ne présumez pas qu’il a tourné — vérifiez qu’il est correct. Exécutez-le sur une entrée dont vous connaissez déjà la réponse et confirmez que la sortie correspond (expect_equal()). Puis soumettez-le à des cas limites — un vecteur vide, un NA, une entrée constante, une valeur limite — car le code généré est généralement juste sur le cas nominal et faux aux bords. Gardez ces contrôles comme tests pour que le comportement reste vérifié. Le runtime, pas la plausibilité du code, est le juge.
Testez vos connaissances
ImportantExercice : faire échouer une fonction bruyamment, puis la tester
Un assistant vous remet pct_change(), qui calcule la variation en pourcentage de old à new. Elle a l’air correcte — mais elle divise par old sans garde-fou, donc old = 0 renvoie Inf silencieusement.
pct_change <-function(old, new) { (new - old) / old *100}pct_change(0, 5) # returns Inf — no error, no warning
Faites deux choses : (1) réécrivez pct_change() pour qu’elle interrompe avec un message clair quand old vaut 0 ; (2) écrivez trois attentes testthat qui vérifient un résultat connu, la borne, et que la mauvaise entrée échoue maintenant.
AstuceIndice
Mettez un garde-fou en tête avec if (old == 0) stop("...") (ou stopifnot("old must be non-zero" = old != 0)). Pour les tests : expect_equal() pour un cas que vous pouvez calculer à la main (de 200 à 250, c’est +25 %), et expect_error(pct_change(0, 5), "...") pour confirmer que le garde-fou se déclenche.
AstuceSolution
library(testthat)pct_change <-function(old, new) {if (old ==0) stop("`old` must be non-zero (cannot compute percent change from 0)") (new - old) / old *100}# 1. a known result: 200 -> 250 is +25%expect_equal(pct_change(200, 250), 25)# 2. a boundary: no change is 0%expect_equal(pct_change(200, 200), 0)# 3. the bad input now aborts loudly instead of returning Infexpect_error(pct_change(0, 5), "must be non-zero")
Les trois sont silencieux, donc le garde-fou fonctionne et l’arithmétique est juste. La fonction refuse désormais l’entrée qui produisait auparavant un Inf silencieux — un échec bruyant que vous voyez vaut mieux qu’un nombre erroné auquel vous ne pouvez pas vous fier.
NoteVérification rapide : quel outil ?
Vous avez écrit une fonction qui résume un data frame, et sur des données réelles elle renvoie un nombre qui paraît trop petit. Elle s’exécute sans aucune erreur ni avertissement. Quelle est la façon la plus directe de découvrir pourquoi ?
A. Envelopper tout le corps de la fonction dans tryCatch(..., error = function(e) NULL) B. Mettre un browser() sur la ligne qui calcule le résumé et inspecter les variables avec str() C. Ajouter d’autres appels warning() et relancer
Afficher la réponse
B. Il n’y a pas d’erreur à attraper (A) ni d’avertissement à émettre (C) — le code s’exécute ; il est simplement faux. Mettre en pause sur la ligne suspecte avec browser() et examiner les objets réels (str() montre leur structure et leur type) révèle le décalage — un NA inattendu, une colonne character et non numeric, un filtre qui a supprimé des lignes. tryCatch et warning() aident avec du code qui casse, pas du code qui se comporte mal en silence.
Conclusion
R digne de confiance est une habitude, pas un package. Rendez les échecs bruyants — stop(), warning(), stopifnot() à la porte — pour que les mauvaises entrées ne puissent pas voyager. Quand quelque chose casse, tournez-vous vers traceback(), browser() et options(error = recover) pour trouver la cause vite, puis passez à autre chose ; le débogage est le chemin, pas la destination. Validez ce qui doit être vrai : affirmez vos données avec pointblank, et testez les fonctions qui produisent vos résultats avec testthat. Et traitez chaque extrait écrit par une IA comme vous traiteriez le vôtre — exécutez-le sur une entrée connue, affirmez la sortie, cassez-le sur les bords, gardez les tests. Un copilote écrit le premier jet en quelques secondes ; le jugement de lui faire confiance, ou non, est la part qui reste la vôtre — and the runtime is the judge.
Leçons connexes
Écrire des fonctions — le signalement stop()/warning() et les vérifications d’entrée sur lesquels cette leçon s’appuie, dans le contexte de l’écriture d’une fonction. · Passer R à l’échelle — le même thème du « jugement qu’une IA ne peut pas feindre » appliqué au code lent. · Contrôle du flux — les gardes if/else derrière chaque assertion et chaque interruption précoce. · Obtenir de l’aide dans R — lire l’erreur, puis trouver le correctif.
Cette leçon est reproductible : chaque résultat de cette page a été produit par le code montré. Copiez n’importe quel bloc et exécutez-le localement pour le reproduire — les tests passent, les rapports de validation et les messages d’erreur sont réels, et les bogues du code IA sont exactement ceux que vous verrez. The runtime is the judge.
@online{2026,
author = {},
title = {Gestion des erreurs en R : déboguer, tester, valider},
date = {2026-07-19},
url = {https://www.datanovia.com/learn/programming/r-foundations/robust-and-trustworthy-r},
langid = {fr}
}