اختبارات Rust: المستوى المتقدم للاختبارات الوحدوية والتكاملية والشاملة (E2E)

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

الاختبارات المتقدمة في Rust: من الاختبارات الوحدوية إلى الاختبارات الشاملة (E2E)

الاختبارات جزء لا يتجزأ من تطوير برامج الأنظمة الموثوقة بلغة Rust. إذا كانت المفاهيم الأساسية (كتابة اختبارات بسيطة باستخدام #[test]) مألوفة للجميع، فإن التقنيات المتقدمة - العمل مع الكائنات الوهمية (mocks)، المعاملات البارامترية، السمات المخصصة، وتنظيم الاختبارات الشاملة (E2E) - تتطلب شرحًا منفصلاً. في هذه المقالة، سننظر في كيفية الارتقاء بالاختبارات في Rust إلى المستوى الاحترافي باستخدام أساليب الاختبارات الوحدوية والتكاملية والشاملة (end-to-end).



1. الاختبارات الوحدوية: الكائنات الوهمية، المعاملات البارامترية، والسمات المخصصة

عادةً ما تُكتب الاختبارات الوحدوية في Rust في نفس ملف الكود، داخل وحدة tests. ومع ذلك، على المستوى المتقدم، تنشأ الحاجة إلى عزل الكود المختبر عن التبعيات الخارجية.



الكائنات الوهمية (Mock objects) باستخدام السمات (Traits)

لإنشاء كائنات وهمية في Rust، غالبًا ما تُستخدم السمات. تقوم بتعريف سمة، وتنفيذها للكائن الحقيقي وللكائن الوهمي الخاص بالاختبار. الحزم الشائعة: mockall و mockito (لـ HTTP).

use mockall::automock;

#[automock]trait Database { fn get_user(&self, id: u32) -> String;}

fn process_user(db: &dyn Database, id: u32) -> String { let user = db.get_user(id); format!("User: {}", user)}

#[cfg(test)]mod tests { use super::*;

#[test] fn test_process_user() { let mut mock = MockDatabase::new(); mock.expect_get_user() .with(predicate::eq(42)) .returning(|_| "Alice".to_string()); assert_eq!(process_user(&mock, 42), "User: Alice"); }}


الاختبارات البارامترية (Parameterized tests)

تكرار نفس الاختبار ببيانات إدخال مختلفة هي مهمة شائعة. توفر حزمة rstest حلاً أنيقًا.

use rstest::rstest;

#[rstest]#[case(2, 3, 5)]#[case(0, 0, 0)]#[case(-1, 1, 0)]fn test_add(#[case] a: i32, #[case] b: i32, #[case] expected: i32) { assert_eq!(a + b, expected);}


سمات الاختبار المخصصة (Custom test attributes)

في بعض الأحيان، تكون هناك حاجة لمنطق قبل أو بعد الاختبار. يمكنك إنشاء سماتك الخاصة من خلال الماكرو الإجرائي (procedural macros)، ولكن من الأسهل استخدام حزمة test-case أو ctor للتهيئة.

// مثال باستخدام ctor لتهيئة المسجل (logger)#[cfg(test)]mod tests {    use ctor::ctor;

#[ctor] fn init() { // تهيئة لمرة واحدة قبل جميع الاختبارات }}


2. اختبارات التكامل: التنظيم والعمل مع التبعيات الخارجية

توجد اختبارات التكامل في Rust في مجلد tests/ على مستوى جذر المشروع. كل ملف هناك هو حزمة منفصلة (crate). تتضمن التقنيات المتقدمة وحدات مشتركة واختبارات مع قواعد بيانات حقيقية أو واجهات برمجة تطبيقات (APIs).



الأدوات المساعدة المشتركة لاختبارات التكامل

لتجنب تكرار الكود (على سبيل المثال، إعداد خادم اختبار)، قم بإنشاء وحدة common داخل tests/.

// tests/common/mod.rspub fn setup_test_db() -> String {    // إعداد قاعدة بيانات اختبار    "connected".to_string()}

// tests/integration_test.rsmod common;

#[test]fn test_db_connection() { let db = common::setup_test_db(); assert_eq!(db, "connected");}


العمل مع طلبات HTTP

لاختبار عملاء HTTP، استخدم mockito - فهو يشغل خادمًا وهميًا محليًا.

use mockito::{mock, server_url};

#[tokio::test]async fn test_api_call() { let _m = mock("GET", "/users/1") .with_status(200) .with_body(r#"{"id":1,"name":"Alice"}"#) .create();

let client = reqwest::Client::new(); let resp = client.get(&format!("{}/users/1", server_url())) .send() .await .unwrap(); assert!(resp.status().is_success());}


3. الاختبارات الشاملة (E2E): دورة حياة التطبيق الكاملة

تتحقق الاختبارات الشاملة (End-to-end) من عمل النظام بأكمله: بدءًا من التشغيل

المدونات

توصيات الكتب