اختبار التكامل لـ Rust مع Docker: المستوى المتقدم
اختبار التكامل هو مرحلة حاسمة في تطوير Rust، خاصة عندما يتفاعل تطبيقك مع خدمات خارجية: قواعد البيانات، قوائم انتظار الرسائل، HTTP-API. استخدام Docker يسمح بإنشاء بيئات معزولة وقابلة للتكرار للاختبارات. في هذه المقالة، سنستعرض تقنيات متقدمة: من الإعداد الأساسي إلى التنفيذ المتوازي للاختبارات باستخدام testcontainers و docker-compose.
لماذا Docker لاختبارات التكامل؟
غالبًا لا تعكس النماذج الوهمية (Mocks) والبدائل (Stubs) التقليدية السلوك الحقيقي للتبعيات. Docker يحل هذه المشكلة:
- العزل: كل اختبار يعمل في حاوية نظيفة.
- قابلية التكرار: بيئة متطابقة على جميع أجهزة المطورين وفي CI.
- التوازي: يمكن تشغيل عدة قواعد بيانات اختبار في وقت واحد.
1. الإعداد الأساسي: الاختبار مع PostgreSQL
لنبدأ بشيء بسيط: سنختبر دالة تعمل مع PostgreSQL. نستخدم crate testcontainers — وهي غلاف Rust لإدارة حاويات Docker.
أضف التبعيات في Cargo.toml:
[dev-dependencies]testcontainers = "0.18"tokio = { version = "1", features = ["full"] }sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] }مثال على اختبار:
use sqlx::PgPool;use testcontainers::clients::Cli;use testcontainers::images::postgres::Postgres;
#[tokio::test]async fn test_database_connection() { let docker = Cli::default(); let postgres_instance = docker.run(Postgres::default()); let connection_string = &format!( "postgres://postgres:postgres@127.0.0.1:{}/postgres", postgres_instance.get_host_port_ipv4(5432) );
let pool = PgPool::connect(connection_string).await.unwrap();
// تنفيذ استعلام SQL let row: (i32,) = sqlx::query_as("SELECT 1 + 1") .fetch_one(&pool) .await .unwrap(); assert_eq!(row.0, 2);}هام: يتم حذف الحاوية تلقائيًا بعد انتهاء الاختبار (بفضل تطبيق Drop).
2. التكوين المتقدم: صورة مخصصة مع الترحيلات (Migrations)
غالبًا ما يكون مطلوبًا تنفيذ الترحيلات مسبقًا. لننشئ صورة مخصصة بناءً على Dockerfile:
# Dockerfile.testFROM postgres:16-alpineCOPY migrations/ /docker-entrypoint-initdb.d/في اختبار Rust، نستخدم Image:
use testcontainers::images::generic::GenericImage;
#[tokio::test]async fn test_with_migrations() { let docker = Cli::default(); let image = GenericImage::new("my-app-test:latest") .with_wait_for(testcontainers::core::WaitFor::message_on_stdout( "database system is ready to accept connections" )); let instance = docker.run(image); // ... نستخدم instance}لبناء الصورة في CI، يمكن استخدام docker build -f Dockerfile.test -t my-app-test:latest .
3. التنفيذ المتوازي للاختبارات مع Docker Compose
إذا كانت خدمتك تتطلب عدة تبعيات (PostgreSQL + Redis)، استخدم docker-compose. أنشئ ملف docker-compose.test.yml:
version: '3.8'services: postgres: image: postgres:16-alpine environment: POSTGRES_PASSWORD: testpass ports: - "5432" redis: image: redis:7-alpine ports: - "6379"في اختبار Rust، نرفع بيئة Compose:
use testcontainers::clients::Cli;use testcontainers::images::compose::Compose;
#[tokio::test]async fn test_with_compose() { let docker = Cli::default(); let compose = Compose::new("docker-compose.test.y