تست قرارداد
1) کجا قراردادها را اعمال کنیم
HTTP REST/JSON: منابع، صفحه بندی، فیلتر، idempotency، کدهای خطا.
gRPC/Protobuf: انواع پیام ها، وضعیت ها، معانی «مهلت»، کامپکت عقب v.proto.
GraphQL: schemas, non-null, directives, permissive to fields.
پیامها/جریانها (Kafka/Pulsar/SQS): طرحهای رویداد (Avro/JSON/Protobuf)، کلیدهای پارتیشن، ترتیب، کلیدهای idempotent.
SDK های داخلی/کتابخانه ها: ویژگی های عمومی/استثنائات/قراردادهای عملکرد.
2) مدل CDC: نقش ها و مصنوعات
مصرف کننده قرارداد انتظارات را منتشر می کند (نمونه درخواست ها/پاسخ ها، نوع matchers، unariants).
تامین کننده تأیید قرارداد را در برابر سرویس/آداپتور/دستگیره های خود اجرا می کند.
کارگزار قرارداد (Pact Broker/Backstage/repo artifact) نسخه ها، برچسب ها («prod»، «staging»، «canary») و ماتریس سازگاری «consumer @ v → provider @ v» را ذخیره می کند.
سیاست انتشار: ارسال یک ارائه دهنده ممنوع است اگر هر قرارداد «تولید مربوطه» نقض شود.
3) چه چیزی را در قرارداد حل کنید (مثال HTTP)
حداقل:- روش/مسیر/پارامترها/هدر (شامل Auth، کلید idempotent).
- بدن و متچرهای معمولی (نوع/فرمت/regexp/محدوده).
- کدهای خطا و ساختار ؛ stable 'error _ code'.
- تغییرناپذیر معنایی: مرتبسازی، یکتایی، یکنواختی 'created _ at'.
- انتظارات غیر کاربردی (اختیاری): p95، محدودیت اندازه، هدر محدود کردن نرخ.
json
{
"interaction": "GET /v1/users/{id}",
"request": { "method": "GET", "path": "/v1/users/123", "headers": {"Accept":"application/json"} },
"matchers": {
"response.body.id": "type:number",
"response.body.email": "regex:^.+@.+\\..+$",
"response.body.created_at": "format:rfc3339"
},
"response": {
"status": 200,
"headers": {"Content-Type":"application/json"},
"body": {"id": 123, "email": "alice@example.com", "created_at": "2025-10-31T12:00:00Z"}
},
"error_cases": [
{
"name":"not_found",
"request":{"path":"/v1/users/9999"},
"response":{"status":404, "body":{"error_code":"USER_NOT_FOUND"}}
}
]
}
4) قراردادهای مبتنی بر رویداد
طرح رویداد: «نوع»، «نسخه»، «شناسه»، «رخ داده است _ at _ UTC»، «تولید کننده»، «موضوع»، «بارگیری».
ثابت: ثابت «id» و idemotence توسط «(نوع، id)»، ترتیب در کلید بخش، یکنواختی «دنباله».
تکامل و سازگاری قوانین (عقب/جلو/کامل).
تست های قرارداد مصرف کننده: پخش رویدادهای «طلایی» و مراحل منفی (زمینه های ناشناخته، nullable).
json
{
"type":"record","name":"UserRegistered","namespace":"events.v1",
"fields":[
{"name":"id","type":"string"},
{"name":"occurred_at_utc","type":{"type":"long","logicalType":"timestamp-millis"}},
{"name":"email","type":"string"},
{"name":"marketing_opt_in","type":["null","boolean"],"default":null}
]
}
5) تکامل و سازگاری
نسخه های قرارداد: معانی «MAJOR». جزئی است. پچ (عمده - شکستن).
قوانین برای استراحت:- خراب نکنید: فیلدها را حذف نکنید، نوع/مقدار 'error _ code' را تغییر ندهید.
- اضافه کردن زمینه های اختیاری با پیش فرض ؛ نقطه پایان جدید به جای «سحر و جادو».
- کاهش: اعلامیه، پشتیبانی همزمان، حذف توسط معیارها.
- GraphQL: فقط فیلدهای اضافه، غیر صفر را از طریق فازها وارد کنید ؛ دستورات را صادر می کند.
- gRPC/Proto: از اعداد فیلد استفاده مجدد نکنید ؛ فقط موارد جدید را با اختیاری اضافه کنید.
- رویدادها: طرح «vN» ؛ مصرف کنندگان ملزم به نادیده گرفتن زمینه های ناشناخته (آرامش) هستند.
6) چک های منفی و ثابت
منفی: انواع نادرست، مقادیر ممنوعه، پارامترهای متضاد، بیش از حد.
ثابت ها: مرتب سازی پاسخ ها، منحصر به فرد بودن «id»، صحت «next _ cursor»، ثبات پاسخ بی نظیر هنگام تکرار.
قراردادهای جنبه های موقت: ایجاد شده در RFC3339/UTC، پیش بینی صحیح روزهای محلی بخشی از قرارداد حمل و نقل نیست - به شرکت های تجاری ارائه می شود.
7) تولید چاقو و توسعه محلی
از قراردادها، پشته ارائه دهنده برای توسعه مصرف کننده تولید می شود.
برای رویدادها - تولید کنندگان پیام های «معتبر/مرزی» با توجه به طرح.
کارکنان با نسخه قرارداد و تاریخ ساخت مشخص شده اند ؛ انتشار در پراد.
8) جاسازی در CI/CD (خط لوله مرجع)
1. CI مصرف کننده:
Lint/build → ایجاد قرارداد → واحد/تست قرارداد → انتشار در کارگزار قرارداد (برچسب: "consumer @ 1. 7. 0`).
2. ارائه دهنده CI:
بالا بردن خدمات به صورت محلی/در ظرف → واکشی قراردادهای مربوطه ('prod '/' staging') → تأیید → انتشار وضعیت در کارگزار.
3. دروازه انتشار:
استقرار ارائه دهنده مسدود شده است اگر قراردادهای برجسته وجود دارد.
4. ماتریس شبانه:
ماتریس سازگاری 'نسخه های مصرف کننده × نسخه های ارائه دهنده' ؛ گزارش ها و هشدارها
9) نمونه هایی از شیوه های دامنه
9. 1 REST: صفحه بندی مکان نما (قرارداد ثابت)
پاسخ شامل 'items []'، 'next _ cursor' (nullable)، 'limit'، 'total' (اختیاری) است.
ثابت: «len (items) ≤ limit»، فراخوانی مکرر با همان «مکاننما» → مجموعه idempotent.
خطا اگر «cursor» و «page» هر دو مشخص شده باشند.
9. 2 پس از idemotency
قرارداد، به سرآیند «کاربرد کلید» نیاز دارد.
Invariant: یک پرس و جو تکراری با همان کلید، همان شناسه/وضعیت را باز می گرداند.
9. 3 رویدادها: تضمین سفارش
کلید پارتیشن در قرارداد "partition _ key = user_id' است.
ثابت: «توالی» به صورت یکنواخت درون کلید افزایش مییابد ؛ مصرف کننده باید پاسخ ها را اداره کند.
10) امنیت و حریم خصوصی در قراردادها
اطلاعات شخصی/اسرار را در نمونه ها قرار ندهید - فقط مصنوعی.
رفع سرصفحه های امنیتی اجباری: «مجوز»، «X-Signature»، «Replay-Prevention».
برای webhooks - قرارداد امضا و پاسخ '2xx '/retrays.
در سیاهههای مربوط از آزمون قرارداد - پوشش زمینه های حساس.
11) ابزار
Pact/Pactflow/Pact Broker - قراردادهای HTTP/Message، ماتریس سازگاری.
OpenAPI/AsyncAPI - مشخصات + ژنراتورهای تست (Dredd، Schemathesis).
کاراته/استراحت مطمئن - چک سناریو از قرارداد REST.
Protobuf/gRPC - 'buf'، 'protolint'، تست سازگاری ؛ رجیستری طرح برای Avro/JSON/Proto در جریان.
آزمون انطباق برای GraphQL (graphql-compat)، آزمون مدار عکس فوری.
12) شبه کد تأیید ارائه دهنده (ساده شده)
python def verify_contract(provider, contract):
for case in contract["cases"]:
req = build_request(case["request"])
res = provider.handle(req) # локально/контейнер assert match_status(res.status, case["response"]["status"])
assert match_headers(res.headers, case["response"].get("headers", {}))
assert match_body(res.body, case["matchers"], allow_extra_fields=True)
for neg in contract.get("error_cases", []):
res = provider.handle(build_request(neg["request"]))
assert res.status == neg["response"]["status"]
assert res.json.get("error_code") == neg["response"]["body"]["error_code"]
13) ضد الگوهای
«تصاویر پستچی یک قرارداد»: هیچ نسخه/matchers معمولی/اعتبار خودکار.
Oversnapping: قرارداد مقادیر دقیق را به جای انواع/الگوهای → false falls اصلاح می کند.
یک قرارداد مشترک برای مناطق/کانال های مختلف: تنوع (پرچم ها، قوانین جغرافیایی) را نادیده می گیرد.
قراردادهای بدون کارگزار/ماتریس: غیر ممکن است که نسخه های سازگار را درک کنید.
شرط بندی در e2e به جای قرارداد: آهسته، گران، ناپایدار است.
بدون موارد منفی/ثابت: تنها «مسیر سبز» تست شده است.
14) قابلیت مشاهده و عملکرد
وضعیت صادرات به کارگزار + داشبورد «قراردادهای بهداشتی».
هشدارها: قطره های جدید در ارائه دهنده در برابر قراردادهای «prod»، افزایش «زمینه ناشناخته» در حوادث.
Trace: 'contract _ id', 'version', 'decision _ id' in verification logs.
15) روند افسردگی
1. یک فیلد/نقطه پایانی (بدون شکستن) اضافه کنید.
2. علامت گذاری به عنوان «منسوخ» در مشخصات، اعلام تاریخ.
3. پیگیری مصرف کنندگان توسط سیاهههای مربوط/کارگزار; راهنمای مهاجرت
4. فعال کردن «سایه» انکار در مرحله (خشک اجرا)، و سپس اجرای.
5. حذف پس از استفاده صفر و سازگاری.
16) چک لیست معمار
1. مصرف کنندگان و صاحبان آنها شناسایی شده اند ؟ قراردادها در حال ثبت شدن هستند ؟
2. آیا یک کارگزار و یک ماتریس سازگاری با برچسب های محیط زیست وجود دارد ؟
3. آیا منفیها و ناورداها (حماقت، نشانگرها، مرتبسازی) در قرارداد گنجانده شدهاند ؟
4. آیا Schema Registry و حالت سازگاری برای رویدادها پیکربندی شده است ؟
5. خط لوله مانع از انتشار ارائه دهنده در صورت نقض قراردادهای تولید می شود ؟
6. آیا روند زوال و سیاست تکامل توصیف شده است ؟
7. چاقوها از قراردادها تولید می شوند، آیا ژنراتورهای رویداد محلی وجود دارد ؟
8. آیا پوشش PD و عنوان ایمنی اجباری مستند شده است ؟
9. معیارها/هشدارها در قراردادها متصل هستند، آیا گزارش های رانش وجود دارد ؟
10. قراردادها در CI توسط هر دو طرف (مصرف کننده و ارائه دهنده) بررسی می شود ؟
نتیجه گیری
تست قرارداد «حقیقت» در مورد تعاملات را به مصنوعات نسخه تبدیل می کند و ادغام را قابل پیش بینی می کند. CDC، کارگزار قرارداد و نظم و انضباط تکامل طرح جایگزین «شگفتی شکستن» با یک فرایند مدیریت: چک سریع، ثابت روشن، و سازگاری نسخه شفاف است. این هزینه e2e را کاهش می دهد، سرعت انتشار و بهبود کیفیت کل پلت فرم.