اختبار API
اختبارات التكامل
اختبار API درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.
لماذا نختبر واجهة API؟
تمنحك الاختبارات الثقة بأن نقاط النهاية تعمل بشكل صحيح وتظل كذلك أثناء تغيير الشيفرة. وبالنسبة إلى واجهة REST API، فإن الاختبارات الأكثر قيمة هي اختبارات التكامل؛ إذ تختبر الموجّه الحقيقي من البداية إلى النهاية، عبر إرسال الطلبات والتحقق من الاستجابات.
يغطي هذا الدرس اختبارات الوحدة، وحيلة oneshot في Tower، واختبارات التكامل الكاملة.
اختبارات الوحدة للمنطق الخالص
يمكن اختبار المنطق الذي لا يتعامل مع الشبكة، مثل التحقق، باستخدام اختبارات وحدة Rust العادية. ضعها في وحدة #[cfg(test)] بجانب الشيفرة. وتعمل هذه الاختبارات بسرعة باستخدام cargo test.
fn validate_title(title: &str) -> bool {
!title.trim().is_empty()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_empty() {
assert!(!validate_title(" "));
assert!(validate_title("buy milk"));
}
}الاختبارات غير المتزامنة
المعالجات غير متزامنة، لذلك يجب أن تعمل دوال الاختبار التي تنتظر نتائج داخل بيئة تشغيل. استخدم #[tokio::test] بدلًا من #[test]. فهي تبدأ بيئة تشغيل Tokio لهذا الاختبار، مما يتيح لك استدعاء الشيفرة غير المتزامنة باستخدام .await.
async fn add(a: i32, b: i32) -> i32 { a + b }
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn adds() {
assert_eq!(add(2, 3).await, 5);
}
}الاختبار دون شبكة
تطبّق موجّهات Axum سمة Service الخاصة بـ Tower، لذلك يمكنك تمرير الطلبات إليها مباشرةً دون ربط منفذ. تأخذ طريقة oneshot كائن Request واحدًا وتعيد كائن Response. يجعل ذلك اختبارات التكامل سريعة وحتمية.
// dev-dependencies needed: tower (for ServiceExt), http-body-util
use axum::{Router, routing::get};
use axum::http::{Request, StatusCode};
use axum::body::Body;
use tower::ServiceExt; // brings in oneshot
async fn check() {
let app = Router::new().route("/health", get(|| async { "ok" }));
let res = app
.oneshot(Request::builder().uri("/health").body(Body::empty()).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}التحقق من رمز الحالة
أول ما تتحقق منه معظم الاختبارات هو حالة HTTP. ينبغي أن يعطي المورد المفقود 404، وأن يعطي الإنشاء الناجح 201، وأن يعطي الجسم غير الصالح 400. أنشئ طلبًا للمسار، وقارن res.status() بالرمز المتوقع.
use axum::http::{Request, StatusCode};
use axum::body::Body;
use tower::ServiceExt;
async fn missing_returns_404(app: axum::Router) {
let res = app
.oneshot(Request::builder()
.uri("/todos/999")
.body(Body::empty()).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::NOT_FOUND);
}قراءة جسم الاستجابة
للتحقق من JSON، اجمع جسم الاستجابة في بايتات ثم أزل تسلسله. يجمع المساعد http_body_util::BodyExt::collect الجسم، ثم تحلّله serde_json إلى نموذجك لإجراء عمليات تحقق على مستوى الحقول.
use http_body_util::BodyExt;
async fn read_json(res: axum::http::Response<axum::body::Body>) {
let bytes = res.into_body().collect().await.unwrap().to_bytes();
let todo: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(todo["done"], false);
}إرسال جسم JSON
لاختبار POST، أنشئ طلبًا بجسم JSON وترويسة نوع المحتوى الصحيحة. حوّل بنية الإدخال إلى تسلسل، واضبط content-type: application/json، ثم مرّر الطلب عبر oneshot.
use axum::http::{Request, StatusCode, header};
use axum::body::Body;
use tower::ServiceExt;
async fn create_returns_201(app: axum::Router) {
let body = serde_json::json!({ "title": "test" }).to_string();
let res = app
.oneshot(Request::builder()
.method("POST").uri("/todos")
.header(header::CONTENT_TYPE, "application/json")
.body(Body::from(body)).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::CREATED);
}منشئ تطبيق اختبار قابل لإعادة الاستخدام
ينبغي أن يبدأ كل اختبار من حالة نظيفة. اكتب دالة مساعدة تنشئ موجّهًا جديدًا بقاعدة بيانات جديدة داخل الذاكرة أو مخصصة للاختبار. يؤدي استدعاؤها في كل اختبار إلى عزل الاختبارات، بحيث لا يتمكن اختبار من التأثير في آخر.
use axum::Router;
use std::sync::{Arc, Mutex};
fn test_app() -> Router {
let store = Arc::new(Mutex::new(Vec::new()));
build(store) // same build() the real server uses
}مجلد tests
توجد اختبارات التكامل في مجلد tests/ على المستوى الأعلى. يُترجم كل ملف فيه بوصفه حزمة منفصلة تستخدم واجهة المكتبة العامة. يجبرك ذلك على الاختبار من خلال الواجهة نفسها التي يراها المستخدمون الحقيقيون.
- يحتوي
tests/api.rsعلى اختبارات نقاط النهاية. - شغّلها جميعًا باستخدام
cargo test.
// tests/api.rs
use my_api::build_router; // exported from lib.rs
#[tokio::test]
async fn health_ok() {
let _app = build_router();
// send a request and assert ...
}الاختبار باستخدام قاعدة بيانات
عندما تستخدم المعالجات sqlx، تحتاج الاختبارات إلى قاعدة بيانات أيضًا. ومن الاستراتيجيات الشائعة: استخدام قاعدة بيانات مخصصة للاختبار، أو معاملات يجري التراجع عنها بعد كل اختبار، أو استخدام ماكرو #[sqlx::test] الخاص بـ sqlx، الذي يجهّز قاعدة بيانات نظيفة لكل اختبار تلقائيًا.
// requires sqlx test features and a DATABASE_URL
use sqlx::PgPool;
#[sqlx::test]
async fn inserts_todo(pool: PgPool) {
let todo = create(&pool, "learn rust").await.unwrap();
assert_eq!(todo.title, "learn rust");
assert_eq!(todo.done, false);
}ما الذي ينبغي اختباره
استهدف مجموعة اختبارات متوازنة:
- المسار الناجح: تعيد الطلبات الصالحة الحالة والجسم الصحيحين.
- الأخطاء: تعيد الموارد المفقودة 404، ويعيد الإدخال غير الصالح 400.
- الحالات الحدّية: القوائم الفارغة، والقيم الحدّية، والتكرارات.
شغّل cargo test في CI لاكتشاف التراجعات قبل النشر.
اختبار سريع
اختبر فهمك لاختبار واجهة API.
مراجعة
لقد تعلّمت اختبار واجهة REST API بلغة Rust:
- اختبر المنطق الخالص باختبارات الوحدة، واستخدم
#[tokio::test]للشيفرة غير المتزامنة. - يشغّل
oneshotالموجّه مباشرةً، دون الحاجة إلى منفذ. - أنشئ الطلبات مع الأجسام والترويسات، واجمع أجسام الاستجابات وحلّلها.
- استخدم منشئ تطبيق جديدًا لكل اختبار للعزل، وضع اختبارات التكامل في
tests/. - يجهّز
#[sqlx::test]قواعد بيانات نظيفة؛ وغطِّ المسارات الناجحة والأخطاء والحالات الحدّية.
الأسئلة الشائعة
هل درس «اختبار API» مجاني؟
نعم — نص درس «اختبار API» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.
ماذا ستتعلم في «اختبار API»؟
اختبارات التكامل تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟
لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «اختبار API»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟
نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.