Tests basés sur les propriétés en Rust : comment attraper des bugs que vous ne cherchiez pas

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Tests basés sur les propriétés en Rust : l'art d'attraper des bugs que vous ne cherchiez pas

\n\n

Avez-vous déjà écrit des tests unitaires, les avez exécutés, tout était vert, puis l'application plantait dès la première entrée non standard d'un utilisateur ? Cela vous dit quelque chose. Les tests classiques, c'est comme vérifier si une serrure fonctionne en n'y insérant que votre propre clé. Et si un intrus (ou simplement le hasard) apportait un tournevis, un trombone ou un morceau de fil de fer ?

\n\n

En Rust, un langage réputé pour sa sécurité, il existe un outil puissant qui permet non seulement de vérifier des scénarios spécifiques, mais de prouver que le code se comporte correctement pour toutes les données d'entrée possibles. Il s'agit des tests basés sur les propriétés. Et aujourd'hui, nous allons les analyser en pratique, avec des exemples concrets, afin que vous puissiez appliquer ces connaissances immédiatement dans vos projets.

\n\n

Qu'est-ce qu'un test basé sur les propriétés et en quoi diffère-t-il d'un test classique ?

\n\n

Imaginez que vous testez une fonction de tri. Un test unitaire classique ressemble à ceci :

\n\n
fn test_sort() {\n    let mut arr = vec![3, 1, 2];\n    arr.sort();\n    assert_eq!(arr, vec![1, 2, 3]);\n}
\n\n

Ça marche ? Ça marche. Mais que se passe-t-il si le tableau est déjà trié ? S'il est vide ? S'il contient 10 000 éléments dans l'ordre inverse ? Écrire des tests pour chaque cas manuellement est une voie sans issue.

\n\n

Les tests basés sur les propriétés inversent l'approche. Au lieu de spécifier des données d'entrée concrètes, vous décrivez une propriété qui doit être vraie pour toute entrée valide. Et une bibliothèque spécialisée (par exemple, proptest ou quickcheck) génère elle-même des centaines et des milliers de cas de test aléatoires, essayant de trouver le seul qui violera votre propriété.

\n\n

Pour un tri, la propriété pourrait être : « Le résultat du tri a toujours la même longueur que le tableau d'origine, et chaque élément du tableau d'origine est présent exactement une fois dans le résultat ». Ou plus simplement : « Le résultat est trié par ordre croissant ».

\n\n

Pourquoi Rust est-il un environnement idéal pour les tests basés sur les propriétés

\n\n

Rust intercepte déjà de nombreuses erreurs à la compilation (gestion de la mémoire, courses de données). Mais il existe une classe de bugs que le compilateur ne voit pas : les erreurs logiques, le traitement incorrect des cas limites, le comportement inattendu avec des combinaisons de données spécifiques. C'est là qu'interviennent les tests basés sur les propriétés en Rust.

\n\n

Ils complètent parfaitement le système de types : les types garantissent que les données ont la bonne structure, et les tests basés sur les propriétés garantissent que la logique de traitement de ces données est correcte pour tout l'espace des valeurs possibles.

\n\n

Boîte à outils : proptest vs quickcheck

\n\n

Dans l'écosystème Rust, il existe deux frameworks principaux : proptest et quickcheck. Tous deux font la même chose, mais avec des philosophies différentes.

\n\n
    \n
  • quickcheck — plus ancien, minimaliste. Il fonctionne via le trait Arbitrary. Vous écrivez une fonction qui prend des données aléatoires, et le test est exécuté plusieurs fois.
  • \n
  • proptest — moderne, puissant, avec de nombreuses stratégies de génération de données. Il permet de configurer finement les données à générer et sait « réduire » (shrink) un exemple échoué au cas reproductible minimal. C'est l'étalon-or aujourd'hui.
  • \n
\n\n

Nous utiliserons proptest, car il offre plus de contrôle et est mieux documenté. Installons-le :

\n\n
# Cargo.toml\n[dev-dependencies]\nproptest = "1.4"
\n\n

Écrire notre premier test basé sur les propriétés en Rust

\n\n

Revenons à notre tri. Écrivons un test qui vérifie que le résultat est effectivement trié :

\n\n
use proptest::prelude::*;\n\nfn is_sorted<T: Ord>(slice: &[T]) -> bool {\n    slice.windows(2).all(|w| w[0] <= w[1])\n}\n\nproptest!                

Blogs

Book Recommendations