Tests d'intégration Rust avec Docker : de A à Z
\n\nLes tests d'intégration sont une étape cruciale dans le développement d'applications Rust fiables. Contrairement aux tests unitaires, les tests d'intégration vérifient l'interaction des composants avec les dépendances externes : bases de données, caches, files d'attente de messages. Docker est idéal pour créer des environnements de test isolés et reproductibles. Dans cet article, nous verrons comment configurer les tests d'intégration d'un projet Rust en utilisant Docker, de l'architecture de base au CI/CD.
\n\n1. Pourquoi Docker est le meilleur choix pour les tests d'intégration Rust
\n\nRust compile en code natif, mais ses applications dépendent souvent de services externes. Docker résout trois problèmes clés :
\n- \n
- Isolation : chaque test s'exécute dans un conteneur propre, éliminant l'influence des exécutions précédentes. \n
- Reproductibilité : des versions précises de PostgreSQL, Redis ou Kafka garantissent un comportement identique sur la machine locale et dans le CI. \n
- Vitesse : les images Docker sont mises en cache et les tests Rust sont compilés une seule fois. \n
Considérons un projet type : un serveur web avec Actix-web, fonctionnant avec PostgreSQL. Notre objectif est d'écrire un test qui démarre un conteneur de base de données, exécute les migrations et vérifie les opérations CRUD.
\n\n2. Configuration de l'infrastructure de test avec Docker Compose
\n\nPour plus de commodité, utilisons un fichier docker-compose.yml qui décrit toutes les dépendances. Créons ce fichier à la racine du projet :
version: '3.8'\nservices:\n postgres_test:\n image: postgres:15-alpine\n environment:\n POSTGRES_USER: test_user\n POSTGRES_PASSWORD: test_pass\n POSTGRES_DB: test_db\n ports:\n - "5433:5432"\n tmpfs: /var/lib/postgresql/data # données en mémoire pour la vitesse\n\nNotez l'utilisation de tmpfs — cela accélère les tests car les données ne sont pas écrites sur le disque. Le port 5433 est choisi pour éviter les conflits avec la base de données locale.
Écrivons maintenant une fonction Rust pour gérer les conteneurs. Utilisons la crate testcontainers — elle permet de lancer des conteneurs Docker par programmation :
// tests/common/mod.rs\nuse testcontainers::{clients, images::postgres::Postgres, Container};\n\npub struct TestDb {\n pub container: Container<'static, Postgres>,\n pub connection_string: String,\n}\n\nimpl TestDb {\n pub fn new() -> Self {\n let docker = clients::Cli::default();\n let postgres_image = Postgres::default();\n let container = docker.run(postgres_image);\n \n let host = container.get_host();\n let port = container.get_host_port_ipv4(5432);\n let connection_string = format!(\n "postgres://postgres:postgres@{}:{}/postgres",\n host, port\n );\n \n TestDb { container, connection_string }\n }\n}\n\nCe module peut être réutilisé dans tous les tests d'intégration. Le conteneur sera automatiquement supprimé en cas d'échec des tests ou à la fin du processus.
\n\n3. Écriture de tests d'intégration avec des requêtes réelles
\n\nCréons un test qui vérifie l'insertion et la lecture d'un utilisateur. Supposons que nous ayons une structure User et les fonctions create_user et get_user dans le code principal.
// tests/integration_test.rs\nmod common;\n\nuse sqlx::PgPool;\nuse your_crate::{create_user, get_user, User};\n\n#[tokio::test]\nasync fn test_create_and_get_user() {\n let test_db = common::TestDb::new();\n let pool = PgPool::connect(&test_db.connection_string)\n .await\n .expect("Échec de connexion à la base de données");\n \n // Exécution des migrations (si vous utilisez sqlx)\n sqlx::migrate!().run(&pool).await.unwrap();\n \n // Création d'un utilisateur\n let user = User {\n name: "Alice".into(),\n email: "alice@example.com".into(),\n };\n let created = create_user(&pool, &us