اختبار التكامل لـ Rust مع Docker: من الألف إلى الياء
\n\nاختبار التكامل هو مرحلة حاسمة في تطوير تطبيقات Rust الموثوقة. على عكس اختبارات الوحدة، تتحقق اختبارات التكامل من تفاعل المكونات مع التبعيات الخارجية: قواعد البيانات، وحدات التخزين المؤقت، قوائم انتظار الرسائل. يعتبر Docker مثاليًا لإنشاء بيئات اختبار معزولة وقابلة للتكرار. في هذه المقالة، سنشرح كيفية إعداد اختبار التكامل لمشروع Rust باستخدام Docker، بدءًا من البنية الأساسية وصولاً إلى CI/CD.
\n\n1. لماذا Docker هو الخيار الأفضل لاختبارات التكامل في Rust
\n\nيتم تجميع Rust إلى كود أصلي، لكن تطبيقاته غالبًا ما تعتمد على خدمات خارجية. يحل Docker ثلاث مشاكل رئيسية:
\n- \n
- العزل: يتم تشغيل كل اختبار في حاوية نظيفة، مما يستبعد تأثير عمليات التشغيل السابقة. \n
- قابلية التكرار: تضمن الإصدارات الدقيقة من PostgreSQL أو Redis أو Kafka سلوكًا متطابقًا على الجهاز المحلي وفي CI. \n
- السرعة: يتم تخزين صور Docker مؤقتًا، ويتم تجميع اختبارات Rust مرة واحدة. \n
لنفكر في مشروع نموذجي: خادم ويب يعمل بـ Actix-web ويتفاعل مع PostgreSQL. هدفنا هو كتابة اختبار يقوم بتشغيل حاوية قاعدة بيانات، وتنفيذ الترحيلات، والتحقق من عمليات CRUD.
\n\n2. إعداد البنية التحتية للاختبار باستخدام Docker Compose
\n\nلتسهيل الأمر، نستخدم ملف docker-compose.yml الذي يصف جميع التبعيات. لننشئ الملف في جذر المشروع:
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 # البيانات في الذاكرة للسرعة\n\nلاحظ استخدام tmpfs — فهذا يسرع الاختبارات لأن البيانات لا تُكتب على القرص. تم اختيار المنفذ 5433 لتجنب التعارض مع قاعدة البيانات المحلية.
الآن، لنكتب دالة Rust لإدارة الحاويات. نستخدم crate testcontainers — والذي يسمح بتشغيل حاويات Docker برمجيًا:
// 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\nيمكن إعادة استخدام هذه الوحدة في جميع اختبارات التكامل. سيتم حذف الحاوية تلقائيًا عند فشل الاختبارات أو انتهاء العملية.
\n\n3. كتابة اختبارات التكامل مع استعلامات حقيقية
\n\nلننشئ اختبارًا يتحقق من إدراج وقراءة مستخدم. لنفترض أن لدينا هيكل User ودوال create_user و get_user في الكود الرئيسي.
// 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("Failed to connect to DB");\n \n // تنفيذ الترحيلات (إذا كنت تستخدم sqlx)\n sqlx::migrate!().run(&pool).await.unwrap();\n \n // إنشاء مستخدم\n let user = User {\n name: "Alice".into(),\n email: "alice@example.com".into(),\n };\n let created = create_user(&pool, &us