Attentes, vérifiées ! Plongez dans l’univers des tests unitaires avec expect_*()

Author : Yohann Mansiaux
Categories : astuces, développement, package
Tags : expect, Tests
Date :

Les tests unitaires sont essentiels dans le développement d’un package R. Ils garantissent que vos fonctions fonctionnent comme prévu, tout en vous protégeant contre les régressions lorsque vous améliorez ou modifiez votre code.

Grâce au package {testthat}, écrire et automatiser des tests en R devient simple et intuitif. Au cœur de cette démarche, les fonctions expect_*() jouent un rôle central en validant les comportements que vous attendez de votre code.

J’ai pas le temps de lire : de quoi ca parle ?

Dans cet article, nous explorerons les fonctions expect_*() les plus utilisées, pourquoi elles sont indispensables et comment les mettre en œuvre pour écrire des tests robustes et maintenables.

Dans un premier temps, chargeons le package :

library(testthat)

Pourquoi utiliser expect_*() ?

Les fonctions expect_*() permettent de comparer le comportement réel de votre code avec les résultats attendus. Chaque test vérifie une “attente” spécifique (d’où le nom “expect”), qu’il s’agisse d’une valeur de retour, d’une erreur, d’un warning ou même d’un message utilisateur. Si une fonction ne répond pas à cette attente, un échec de test est signalé, vous alertant d’un problème potentiel.

Voyons maintenant les fonctions expect_*() les plus couramment utilisées pour tester différents aspects de votre code.

expect_equal()

  • Utilité :

Vérifie que deux objets sont égaux en valeur, avec une tolérance aux différences mineures (par exemple, l’imprécision numérique).

  • Exemple :
test_that("Multiplication works", {
  result <- 2 * 3
  expect_equal(object = result, expected = 6)
})
#> Test passed 🎉

expect_identical()

  • Utilité :

Vérifie que deux objets sont strictement identiques, y compris leurs attributs et leur structure.

  • Exemple :
test_that("Les objets sont strictement identiques", {
  list1 <- list(a = 1, b = 2)
  list2 <- list(a = 1, b = 2)
  expect_identical(object = list1, expected = list2)
})
#> Test passed 🥳

Contrairement à expect_equal(), ici le test échoue si les objets diffèrent par un attribut ou un détail, par exemple une imprécision numérique :

test_that("expect_identical vs expect_equal avec tolérance numérique", {
  num1 <- 0.1 + 0.2
  num2 <- 0.3
  # Test avec expect_equal : Ce test va passer grâce à une petite tolérance numérique
  expect_equal(object = num1, expected = num2)
  # Test avec expect_identical : Ce test va échouer car la comparaison est strictement exacte
  expect_identical(object = num1, expected = num2)
})

expect_true() et expect_false()

  • Utilité :

Vérifie qu’une expression logique est vraie ou fausse.

  • Exemple :

test_that("Test logique vrai et faux", {
  expect_true(object = (1 + 1 == 2))
  expect_false(object = (1 + 1 == 3))
})
#> Test passed 🎊

expect_error()

  • Utilité :

Vérifie qu’une fonction déclenche une erreur. Vous pouvez également préciser le message d’erreur attendu pour une validation plus fine.

  • Exemple :
test_that("Erreur lorsque l'entrée est négative", {
  expect_error(object = log("fake"), regexp = "non-numeric argument to mathematical function")
})

Ici, le test passe uniquement si l’exécution de log("fake") génère une erreur avec le message “non-numeric argument to mathematical function”.

⚠️ : Ne prendre que le message d’erreur sans la partie “Error in … :”

expect_message()

  • Utilité :

Vérifie qu’une fonction génère un message via la fonction message() et valide ainsi que votre code informe correctement l’utilisateur.

  • Exemple :
test_that("Message affiché correctement", {
  expect_message(object = message("Salut !"), regexp = "Salut !")
})
#> Test passed 😀

expect_length()

  • Utilité :

Vérifie que la longueur d’un objet (vecteurs, listes ou autres objets) est égale à la valeur attendue.

  • Exemple :
test_that("Le vecteur a la longueur correcte", {
  vec <- c(1, 2, 3)
  expect_length(object = vec, n = 3)
})
#> Test passed 🌈

expect_type()

  • Utilité :

Vérifie que l’objet testé est du type attendu (comme “double”, “character”, “data.frame”, …). C’est utile pour valider que les fonctions renvoient des objets du type approprié.

  • Exemple :
test_that("Le type de l'objet est correct", {
  num <- 42
  expect_type(object = num, type = "double")
  vec <- c(TRUE, FALSE, TRUE)
  expect_type(object = vec, type = "logical")
})
#> Test passed 🥇

Conclusion : Testez vos attentes avec précision

Les fonctions expect_*() du package {testthat} sont des outils puissants pour écrire des tests unitaires en R. Elles vous permettent de couvrir une large gamme de cas d’utilisation, qu’il s’agisse de vérifier des valeurs numériques, des erreurs, des avertissements ou des types d’objets. En intégrant ces tests dans votre flux de travail, vous augmentez la robustesse de votre code et simplifiez sa maintenance à long terme.

Alors, qu’attendez-vous ? Plongez dans l’univers des tests unitaires, écrivez vos premières attentes avec expect_*() et assurez-vous que vos fonctions R respectent toujours les attentes définies !

Pour aller plus loin

Pour ceux qui souhaitent aller plus loin et maîtriser l’art de créer des packages robustes, incluant les bonnes pratiques de tests unitaires, jetez un coup d’oeil sur notre formation – R Niveau 2 – Développeur – Création de packages


À propos de l'auteur

Yohann Mansiaux

Yohann Mansiaux

Data Scientist au pays des m'R'veilles


Comments


Also read