Contactez-nous

Les macros d'assertion : `assert!`, `assert_eq!`, `assert_ne!`

Apprenez à utiliser efficacement les macros d'assertion `assert!`, `assert_eq!`, et `assert_ne!` en Rust pour écrire des tests unitaires clairs, précis et fiables.

Les macros d'assertion : le coeur de la vérification en tests Rust

Lors de l'écriture de tests unitaires en Rust, l'objectif principal est de vérifier que le code se comporte comme prévu. Pour ce faire, le framework de test de Rust fournit une série de macros d'assertion. Ces macros sont des outils qui permettent de déclarer une condition qui doit être vraie pour que le test réussisse. Si la condition évaluée par une macro d'assertion s'avère fausse, la macro déclenche une panique (`panic!`), ce qui interrompt l'exécution du test courant et le marque comme ayant échoué.

Les macros d'assertion sont donc essentielles pour exprimer les attentes de vos tests. Elles rendent explicites les invariants que votre code doit respecter. Les trois macros d'assertion les plus fondamentales et les plus fréquemment utilisées sont `assert!`, `assert_eq!`, et `assert_ne!`. Comprendre leur fonctionnement et savoir quand les utiliser est crucial pour écrire des tests efficaces et informatifs.

L'un des avantages de ces macros est qu'en cas d'échec, elles fournissent souvent des informations de débogage utiles, comme les valeurs qui ont causé l'échec de l'assertion, facilitant ainsi l'identification et la correction des bugs.

La macro `assert!` : vérifier une condition booléenne

La macro `assert!(expression)` est la plus simple des macros d'assertion. Elle prend en argument une expression booléenne. Si l'expression s'évalue à `true`, l'assertion passe et le test continue. Si l'expression s'évalue à `false`, la macro `assert!` panique, et le test échoue.

Syntaxe de base :

assert!(condition_booléenne);

Par exemple, pour tester une fonction qui vérifie si un nombre est positif :

fn est_positif(nombre: i32) -> bool {
    nombre > 0
}

#[test]
fn test_nombre_positif() {
    assert!(est_positif(10)); // Succès, car 10 > 0 est true
    // assert!(est_positif(-5)); // Echec, car -5 > 0 est false, le test paniquerait ici
}

#[test]
fn test_avec_variable_bool() {
    let est_valide = true;
    assert!(est_valide);
}

Il est également possible d'ajouter un message de formatage personnalisé à `assert!` qui sera affiché si l'assertion échoue. Ce message peut inclure des valeurs pour fournir plus de contexte sur l'échec. La syntaxe est similaire à celle de la macro `println!`.

assert!(expression, "Message d'erreur si échec avec des arguments de formatage comme {} ou {:?}", arg1, arg2, ...);

Exemple avec message personnalisé :

#[test]
fn test_verification_limite() {
    let valeur = 150;
    let limite = 100;
    assert!(
        valeur <= limite,
        "La valeur {} a dépassé la limite autorisée de {}",
        valeur,
        limite
    ); // Ce test échouera car 150 n'est pas <= 100
}

En cas d'échec de ce test, le message affiché serait : "La valeur 150 a dépassé la limite autorisée de 100".

Les macros `assert_eq!` et `assert_ne!` : comparer des valeurs

Alors que `assert!` est utile pour les conditions booléennes générales, il est très fréquent dans les tests de devoir comparer une valeur obtenue par le code testé avec une valeur attendue. Pour cela, les macros `assert_eq!` (assert equals) et `assert_ne!` (assert not equals) sont plus adaptées et fournissent de meilleurs messages d'erreur.

`assert_eq!(gauche, droite)` : Cette macro vérifie si les expressions `gauche` et `droite` sont égales. Elle utilise l'opérateur `==` (donc les types des opérandes doivent implémenter le trait `PartialEq`). Si `gauche == droite` est vrai, l'assertion passe. Sinon, elle panique et le test échoue. L'avantage principal de `assert_eq!` par rapport à `assert!(gauche == droite)` est que si l'assertion échoue, le message de panique affichera les valeurs de `gauche` et `droite`, ce qui est extrêmement utile pour le débogage.

// Syntaxe de base
assert_eq!(valeur_obtenue, valeur_attendue);

// Avec message personnalisé (affiché en plus des valeurs de gauche et droite)
assert_eq!(valeur_obtenue, valeur_attendue, "Message d'erreur si différentes");

Exemple :

fn additionner(a: i32, b: i32) -> i32 {
    a + b
}

#[test]
fn test_addition() {
    assert_eq!(additionner(2, 3), 5); // Succès
    // assert_eq!(additionner(2, 3), 6); // Echec, message : "assertion failed: `(left == right)`
                                        // left: `5`,
                                        // right: `6`"
}

#[test]
fn test_comparaison_chaines() {
    let resultat = String::from("hello");
    assert_eq!(resultat, "hello");
}

`assert_ne!(gauche, droite)` : Cette macro est l'opposé de `assert_eq!`. Elle vérifie si les expressions `gauche` et `droite` ne sont pas égales. Elle utilise l'opérateur `!=` (les types doivent implémenter `PartialEq`). Si `gauche != droite` est vrai, l'assertion passe. Si elles sont égales, la macro panique et le test échoue. Comme `assert_eq!`, elle affiche les valeurs des deux côtés en cas d'échec.

// Syntaxe de base
assert_ne!(valeur1, valeur2);

// Avec message personnalisé
assert_ne!(valeur1, valeur2, "Message d'erreur si égales");

Exemple :

#[test]
fn test_non_egalite() {
    let x = 10;
    let y = 20;
    assert_ne!(x, y); // Succès
    // assert_ne!(x, 10); // Echec, message : "assertion failed: `(left != right)`
                         // left: `10`,
                         // right: `10`"
}

L'utilisation de `assert_eq!` et `assert_ne!` est généralement préférée à `assert!` pour les comparaisons d'égalité car leurs messages d'erreur sont plus informatifs. Elles permettent de voir immédiatement quelles étaient les valeurs attendues et obtenues, accélérant ainsi le processus de diagnostic en cas d'échec d'un test.

Choisir la bonne macro d'assertion et bonnes pratiques

Le choix de la macro d'assertion dépend de ce que vous souhaitez vérifier :

  • Utilisez `assert!(condition)` lorsque vous testez une condition booléenne simple (par exemple, si un booléen est vrai, si un nombre est dans une certaine plage, si un vecteur n'est pas vide après une opération).
  • Utilisez `assert_eq!(obtenu, attendu)` lorsque vous voulez vérifier qu'une valeur produite par votre code est exactement égale à une valeur attendue. C'est la macro la plus courante pour vérifier les résultats de fonctions.
  • Utilisez `assert_ne!(valeur1, valeur2)` lorsque vous voulez vérifier que deux valeurs sont différentes.

Quelques bonnes pratiques lors de l'utilisation des macros d'assertion :

  • Soyez précis : Une assertion par aspect testé. Si une fonction doit vérifier plusieurs choses, il peut être préférable d'avoir plusieurs assertions ou même plusieurs tests plus petits et ciblés.
  • Messages d'erreur clairs : Lorsque vous utilisez le message personnalisé optionnel, rendez-le informatif. Expliquez ce qui était attendu et pourquoi l'échec est significatif.
  • Testez les cas limites et les erreurs : N'oubliez pas de tester les cas extrêmes, les entrées invalides (avec `#[should_panic]` si pertinent) et les conditions d'erreur.
  • Lisibilité : La lisibilité de vos tests est aussi importante que celle de votre code de production. Des assertions claires y contribuent grandement.

En maîtrisant `assert!`, `assert_eq!`, et `assert_ne!`, vous disposez des outils fondamentaux pour écrire des tests unitaires robustes et expressifs en Rust, ce qui est une étape clé vers la production d'un logiciel fiable et de haute qualité.