لغة Rust تمنحك خادم HTTP سريعًا وآمنًا من حيث الأنواع بسرعة، لكنها لا تمنحك دائمًا حلقة تغذية راجعة سريعة لاختبار عقد الـ API. cargo test مهم، لكنه يدور حول الكود لا حول HTTP الفعلي. لاختبار أكواد الحالة، شكل JSON، الرؤوس، JWT، وطلبات الواجهة الأمامية قبل اكتمال المعالج، تحتاج أداة تعمل خارج سلسلة أدوات Rust وتتحدث مع الخادم قيد التشغيل.
هذا الدليل يوضح سير عمل عملي لاختبار واجهة برمجة تطبيقات Rust داخل Apidog: توصيل Apidog بخادم Axum أو Actix، إنشاء طلبات، التحقق من JSON المُسلسل بواسطة Serde، التعامل مع JWT، إنشاء mocks للواجهة الأمامية، ثم تشغيل كل ذلك في CI. إذا كنت تستخدم Postman أو curl، فستحصل أيضًا على مواصفات OpenAPI، عناوين mock قابلة للمشاركة، وبيئات عمل جماعية. يمكنك قراءة قصة ترحيل Postman لاحقًا؛ هنا نركز على Rust.
ملخص سريع
- شغّل خادم Rust محليًا باستخدام
cargo run. - أضف
http://localhost:3000كـbaseUrlفي بيئة Apidog. - أنشئ طلب
GET /healthzكاختبار دخان. - اختبر JSON القادم من Serde باستخدام assertions بعد كل طلب.
- خزّن JWT في متغير
{{token}}وطبّق Bearer Auth على مستوى المجلد. - استخدم Mock endpoints حتى تعمل الواجهة الأمامية قبل اكتمال المعالج.
- احفظ الطلبات كسيناريو اختبار وشغّلها في CI باستخدام
apidog-cli.
لماذا تختبر Rust API خارج cargo test؟
cargo test ممتاز لاختبارات الوحدة والتكامل داخل Rust، لكنه ليس دائمًا أفضل طبقة لاختبار عقد HTTP العام.
إذا أردت التأكد من أن المعالج يعيد:
- Status code صحيحًا
- JSON بالشكل المتوقع
- Headers صحيحة
- رسالة خطأ واضحة عند الإدخال غير الصحيح
- سلوكًا ثابتًا أمام الواجهة الأمامية
فستحتاج عادة إلى كتابة اختبارات HTTP داخل Rust أو استخدام curl يدويًا. هذا يعمل، لكنه يصبح مكلفًا مع تغيّر المعالجات.
Apidog يضيف طبقة عقد فوق الخادم قيد التشغيل:
- الطلبات محفوظة وقابلة لإعادة التشغيل.
- التأكيدات موجودة بجانب الطلب.
- الفريق كله يرى نفس العقود.
- يمكن تشغيل نفس السيناريو في CI.
- يمكن توليد OpenAPI من الطلبات المحفوظة.
الفكرة: اترك cargo test لاختبار كود Rust، واستخدم Apidog لاختبار واجهة HTTP كما يراها العملاء.
الخطوة 1: أضف خادم Rust كبيئة في Apidog
ابدأ بخادم Axum بسيط:
use axum::{routing::get, Router};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
let app = Router::new().route("/healthz", get(|| async { "ok" }));
let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
شغّله:
cargo run
في Apidog:
- أنشئ مشروعًا جديدًا.
- افتح إدارة البيئات.
- أضف بيئة باسم
Rust Local.
| المتغير | القيمة |
|---|---|
baseUrl |
http://localhost:3000 |
token |
اتركها فارغة الآن |
apiVersion |
v1 |
أضف بيئة ثانية باسم Rust Staging واستخدم عنوان URL التجريبي. بهذه الطريقة يمكنك التبديل بين المحلي والتجريبي بدون تعديل كل طلب يدويًا.
الخطوة 2: أنشئ أول طلب
داخل المشروع، أنشئ مجلدًا باسم Rust API، ثم أضف طلبًا جديدًا:
- Method:
GET - URL:
{{baseUrl}}/healthz
اضغط Send.
النتيجة المتوقعة:
200 OK
ok
احفظ الطلب باسم:
health-check
هذا الطلب هو أبسط اختبار دخان. إذا فشل، لا تكمل بقية الاختبارات قبل إصلاح الاتصال بالخادم.
إذا ظهر خطأ connection refused، تحقق من:
- أن الخادم يعمل.
- أن المنفذ صحيح.
- أن الخادم مربوط بـ
0.0.0.0:3000وليس فقط127.0.0.1:3000، خصوصًا إذا كنت تستخدم Docker أو بيئة محلية مختلفة.
الخطوة 3: اختبر طلب واستجابة JSON باستخدام Serde
أضف مسارًا لإنشاء مستخدم:
use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Serialize)]
struct User {
id: u64,
name: String,
email: String,
}
async fn create_user(Json(payload): Json<CreateUser>) -> Json<User> {
Json(User {
id: 1,
name: payload.name,
email: payload.email,
})
}
let app = Router::new().route("/users", post(create_user));
في Apidog، أنشئ طلبًا جديدًا:
- Method:
POST - URL:
{{baseUrl}}/users - Body: JSON
{
"name": "Ada Lovelace",
"email": "ada@example.com"
}
أرسل الطلب واحفظه باسم:
create-user
أضف assertions في تبويب Tests:
pm.test("Status is 200", () => {
pm.expect(pm.response.code).to.eql(200);
});
pm.test("Body has id, name, email", () => {
const body = pm.response.json();
pm.expect(body).to.have.property("id");
pm.expect(body.name).to.eql("Ada Lovelace");
pm.expect(body.email).to.match(/^[^@]+@[^@]+$/);
});
الآن إذا تغيّر شكل الاستجابة لاحقًا بسبب serde، مثل إضافة:
#[serde(rename_all = "camelCase")]
فسيفشل اختبار العقد قبل أن يصل التغيير إلى الإنتاج.
الخطوة 4: غطِّ حالات رفض Serde
لا تختبر المسار السعيد فقط. اختبر المدخلات الخاطئة أيضًا.
أنشئ الطلبات التالية في Apidog:
| الطلب | Body | المتوقع |
|---|---|---|
create-user-missing-email |
{ "name": "Ada" } |
422، مع رسالة عن الحقل المفقود |
create-user-extra-field |
{ "name": "Ada", "email": "a@b.c", "admin": true } |
200 إذا لم تستخدم deny_unknown_fields، أو 422 إذا استخدمته |
create-user-wrong-type |
{ "name": 1, "email": "a@b.c" } |
422، مع رسالة عن النوع غير الصحيح |
مثال assertion:
pm.test("Status is 422", () => {
pm.expect(pm.response.code).to.eql(422);
});
هذه الاختبارات توثق سياسة التحقق الفعلية. إذا قررت لاحقًا إضافة:
#[serde(deny_unknown_fields)]
فسيتغير سلوك extra-field، وسيظهر ذلك فورًا في Apidog وCI.
الخطوة 5: اختبر المسارات المحمية بواسطة JWT
غالبًا ما تكون واجهات Rust الإنتاجية محمية بطبقة مصادقة. مثال مبسط باستخدام JWT:
use axum::{Json, http::StatusCode};
use axum_extra::extract::cookie::PrivateCookieJar;
use jsonwebtoken::{decode, DecodingKey, Validation};
async fn me(jar: PrivateCookieJar) -> Result<Json<User>, StatusCode> {
let token = jar
.get("token")
.ok_or(StatusCode::UNAUTHORIZED)?;
let claims = decode::<Claims>(
token.value(),
&DecodingKey::from_secret(b"secret"),
&Validation::default(),
)
.map_err(|_| StatusCode::UNAUTHORIZED)?;
Ok(Json(User {
id: claims.claims.sub,
name: "Ada".into(),
email: "ada@example.com".into(),
}))
}
بدل إنشاء JWT يدويًا لكل طلب، أضف Pre-Request Script على مستوى المجلد في Apidog:
const jwt = require("jsonwebtoken");
const token = jwt.sign(
{
sub: 1,
exp: Math.floor(Date.now() / 1000) + 3600
},
"secret"
);
pm.environment.set("token", token);
ثم من إعدادات المجلد:
- Auth Type:
Bearer Token - Token:
{{token}}
الآن ترث كل الطلبات داخل المجلد نفس إعداد المصادقة، ويتم توليد JWT جديد قبل التشغيل.
للمزيد حول اختبار JWT، راجع كيفية اختبار مصادقة JWT في واجهات برمجة التطبيقات.
الخطوة 6: اختبر Streaming و Server-Sent Events
يدعم Axum وActix التدفق كجزء أساسي من HTTP. في Axum، يمكن أن يعيد المعالج Sse مبنيًا على futures::Stream.
تنسيق SSE عادة يكون:
data: { ... }
data: { ... }
event: done
data: {}
في Apidog:
- أنشئ طلب
GETلنقطة SSE. - أرسله.
- إذا كان
Content-Typeهوtext/event-stream، ستظهر الاستجابة في وضع streaming. - راقب كل frame مع زمن وصوله.
ما يجب اختباره:
- أول event يصل ضمن الزمن المتوقع.
- event معين مثل
event: doneيصل قبل إغلاق الاتصال. - الاتصال لا يبقى مفتوحًا للأبد.
- يمكن ضبط timeout للطلب حتى يفشل الاختبار عند التدفق غير المنتهي.
إذا كنت تستخدم WebSocket بدل SSE، استخدم نوع طلب WebSocket في Apidog واحفظ تسلسل الرسائل والاستجابات بنفس الفكرة.
الخطوة 7: أنشئ Mock API لتطوير الواجهة الأمامية بالتوازي
الواجهة الأمامية لا تحتاج انتظار اكتمال كل معالج Rust. يمكنها العمل ضد mock ثابت طالما أن العقد واضح.
في Apidog:
- افتح طلب
create-user. - اختر Smart Mock.
- فعّله.
سيُنشئ Apidog endpoint مثل:
https://mock.apidog.com/m1/<projectId>/users
ويمكن للواجهة الأمامية إرسال POST إليه بنفس شكل الطلب الحقيقي.
لـ mock ديناميكي، استخدم Advanced Mock:
return {
id: Math.floor(Math.random() * 10000),
name: body.name,
email: body.email,
createdAt: new Date().toISOString()
};
النتيجة: الواجهة الأمامية تحصل على id وcreatedAt واقعيين بدون انتظار معالج Rust.
عندما يصبح المعالج جاهزًا، تغيّر الواجهة الأمامية baseUrl من mock إلى:
http://localhost:3000
ولا تحتاج لتغيير شكل الطلب.
لنفس النمط في بيئات أخرى، راجع بناء واختبار واجهة برمجة تطبيقات Spring Boot وسير عمل اختبار واجهة برمجة التطبيقات العام.
الخطوة 8: احفظ الطلبات كسيناريو CI
حوّل الطلبات إلى سيناريو اختبار قابل للتشغيل بدون واجهة رسومية.
مثال تسلسل:
health-check
تأكيد200.create-user
تأكيد200، ثم خزّنbody.idفي متغير.create-user-missing-email
تأكيد422.me
يستخدم JWT من Pre-Request Script، ثم يؤكد200.طلب SSE
يؤكد أن التدفق يكتمل خلال 5 ثوانٍ.
مثال لتخزين id بعد إنشاء المستخدم:
const body = pm.response.json();
pm.environment.set("userId", body.id);
ثم يمكن استخدامه لاحقًا:
pm.test("Returned id matches created user", () => {
const body = pm.response.json();
pm.expect(String(body.id)).to.eql(String(pm.environment.get("userId")));
});
صدّر السيناريو كملف JSON وضعه في المستودع:
tests/apidog/contract.json
مثال GitHub Actions:
- name: Run API contract tests
run: |
cargo build --release
./target/release/myserver &
sleep 2
apidog-cli run tests/apidog/contract.json --env "Rust Local"
الآن كل Pull Request يلمس معالجًا يحصل على اختبار عقد فعلي ضد binary قيد التشغيل.
الخطوة 9: أنشئ OpenAPI من الطلبات المحفوظة
بعد استقرار الطلبات:
- افتح قائمة Export في Apidog.
- اختر OpenAPI 3.1.
- صدّر المواصفة.
ستحصل على ملف مواصفات مبني من الطلبات والأمثلة التي اختبرتها فعليًا.
يمكنك أيضًا تشغيل التصدير من CI:
apidog-cli export --format openapi --output openapi.json
ثم أضف openapi.json إلى المستودع حتى يستخدمه من يحتاج إلى توليد عملاء TypeScript أو Swift أو Kotlin أو Python.
الأسئلة الشائعة
هل يعمل Apidog مع Axum وActix-web؟
نعم. Apidog يتعامل مع HTTP، لذلك يعمل مع Axum وActix-web وRocket وWarp وPoem وLoco. النقطة المهمة في التطوير المحلي هي ربط الخادم بـ 0.0.0.0 عند الحاجة.
كيف أختبر المعالجات التي تتعطل؟
يمكنك وضع CatchPanicLayer من tower-http أمام الـ router لتحويل panic إلى 500. بعدها أنشئ طلبًا في Apidog يؤدي إلى هذا المسار وتأكد من status code.
إذا لم تغلف حالات panic، قد يسقط الاتصال ويظهر خطأ شبكة. هذا أيضًا سلوك عقد يمكن توثيقه.
هل يمكن تشغيل Apidog ضد binary داخل Docker؟
نعم. اجعل baseUrl يشير إلى المنفذ المكشوف للحاوية. إذا كنت تستخدم Docker Compose، تأكد أن مشغل Apidog يستطيع الوصول إلى نفس الشبكة أو استخدم المنفذ المربوط بالمضيف.
ماذا عن gRPC؟
Apidog يدعم نوع طلب gRPC. استورد ملفات .proto، اختر service وmethod، املأ request body، ثم أرسل. نفس مفاهيم البيئات والمصادقة وسيناريوهات الاختبار تنطبق أيضًا.
هل يغني ذلك عن cargo test؟
لا. استخدم cargo test لاختبارات منطق Rust الداخلي. استخدم Apidog لاختبار العقد التشغيلي عبر HTTP.
الطبقتان تكملان بعضهما:
-
cargo testيكتشف دالة مكسورة. - Apidog يكتشف JSON خاطئًا، status code مختلفًا، header مفقودًا، أو سلوك مصادقة غير متوقع.
هل Apidog مجاني لمشاريع Rust مفتوحة المصدر؟
نعم. عميل Apidog مجاني للأفراد والفرق الصغيرة، وتتضمن الطبقة المجانية سيناريوهات الاختبار، المحاكاة، وتصدير OpenAPI.
الخلاصة
واجهات Rust تحتاج اختبارًا لا ينتظر المترجم في كل مرة. باستخدام Apidog يمكنك بناء مجموعة طلبات تتحقق من HTTP الحقيقي، JSON الحقيقي، JWT، mocks للواجهة الأمامية، وسيناريو CI يعمل ضد binary فعلي.
ابدأ بـ GET /healthz، أضف طلبات JSON، غطِّ حالات Serde الفاشلة، ثم شغّل السيناريو في CI. بعد ذلك يصبح كل تغيير في Axum أو Actix اختبار عقد واضحًا بدل مفاجأة في وقت التشغيل.
قم بتنزيل Apidog ووجهه إلى خادم Rust الخاص بك. الإعداد يستغرق دقائق، والنتيجة عقد API مستقل عن cargo وقابل للمشاركة مع الفريق.
Top comments (0)