یک تست End-to-End سبز شده، اما همان نسخهٔ سرویس پرداخت چند دقیقه بعد با نسخهٔ قدیمی سرویس سفارش ناسازگار است. تست فقط یک ترکیب نسخه و یک مسیر خوش‌بینانه را دیده؛ در محیط واقعی، Timeout، پیام تکراری، Callback دیررس و استقرار مستقل سرویس‌ها وارد بازی می‌شوند. مسئلهٔ تست میکروسرویس‌ها «تست بیشتر» نیست؛ مسئله این است که هر ریسک در نزدیک‌ترین مرز، با ارزان‌ترین شاهد معتبر آزموده شود.

در این راهنما یک استراتژی اجرایی برای تست میکروسرویس‌ها می‌سازیم: از Unit و Component تا Contract، Integration، Journey و آزمون کنترل‌شده پس از استقرار. مثال اصلی، جریان سفارش یک فروشگاه ایرانی است که میان Checkout، Inventory، Payment و Order حرکت می‌کند و باید خطاهای شبکه، ناسازگاری نسخه، Eventual Consistency و تکرار Callback بانکی را تاب بیاورد.

پاسخ کوتاه: استراتژی مؤثر تست میکروسرویس، منطق هر سرویس را در انزوا سریع می‌آزماید، قرارداد مصرف‌کننده و ارائه‌دهنده را در CI تأیید می‌کند، اتصال به زیرساخت واقعی را با Integration Test می‌سنجد، چند Journey حیاتی را End-to-End اجرا می‌کند و با Trace، Synthetic و Canary رفتار نسخهٔ مستقر را مشاهده می‌کند. هیچ لایه‌ای جای همه لایه‌های دیگر را نمی‌گیرد.

تست میکروسرویس چیست؟

Microservice Testing مجموعه‌ای از فعالیت‌های تست در چند دامنه است: کد داخل سرویس، رابط‌های همگام، پیام‌های ناهمگام، پایگاه دادهٔ تحت مالکیت سرویس، پیکربندی و زیرساخت، تعامل بین نسخه‌ها و رفتار کل Journey. تفاوت اصلی با یک برنامهٔ تک‌فرایندی، وجود مرزهای شبکه و استقرار مستقل است.

این استقلال، آزادی انتشار می‌دهد اما ماتریسی از نسخه‌ها می‌سازد. Consumer نسخهٔ A ممکن است هم‌زمان با Provider نسخهٔ B یا C کار کند. پاسخ موفق HTTP نیز لزوماً به معنی تکمیل کسب‌وکار نیست؛ ممکن است سفارش ایجاد شود و رزرو موجودی چند ثانیه بعد از طریق Event نهایی گردد.

نیت جست‌وجو و کلیدواژه‌ها

کلیدواژهٔ اصلی «تست میکروسرویس‌ها» است. عبارت‌های مکمل شامل «استراتژی تست میکروسرویس»، «Microservice Testing»، «تست قرارداد»، «Consumer-driven Contract Testing»، «تست سرویس»، «تست معماری Event-driven»، «تست Saga»، «Integration Testing میکروسرویس» و «E2E میکروسرویس» هستند. این مقاله برای QA فنی، SDET، توسعه‌دهنده، Platform Engineer و Tech Lead نوشته شده است.

چرا تست میکروسرویس‌ها دشوارتر است؟

  • شکست جزئی: یک سرویس سالم است، سرویس پایین‌دست Timeout می‌دهد و Broker با تأخیر پیام را تحویل می‌دهد.
  • عدم قطعیت شبکه: تأخیر، قطع اتصال، Retry و پاسخ دیررس بخشی از رفتار سیستم‌اند.
  • نسخه‌های هم‌زیست: Rolling Deployment برای مدتی چند نسخه را هم‌زمان نگه می‌دارد.
  • دادهٔ توزیع‌شده: تراکنش ACID سراسری معمولاً وجود ندارد و سازگاری ممکن است نهایی باشد.
  • تعامل همگام و ناهمگام: REST یا gRPC، Queue، Event و Callback مدل‌های شکست متفاوتی دارند.
  • مالکیت چندتیمی: قرارداد، زمان انتشار و شواهد تست میان تیم‌ها تقسیم می‌شود.
  • زیرساخت اجرایی: Service Discovery، Policy، Secret، Probe و Config در رفتار واقعی نقش دارند.

پس «همه سرویس‌ها را بالا بیاور و E2E بزن» هم کند است و هم تشخیص خطا را سخت می‌کند. باید Boundaryهای معماری را به نقاط آزمون تبدیل کرد.

مدل نمونه: سفارش و پرداخت در فروشگاه ایرانی

سناریوی نمونه پنج جزء دارد:

  1. Checkout Service درخواست خرید را دریافت می‌کند.
  2. Inventory Service موجودی را رزرو می‌کند.
  3. Payment Service درخواست پرداخت را به PSP می‌فرستد و Callback را دریافت می‌کند.
  4. Order Service وضعیت سفارش را از Eventها می‌سازد.
  5. Notification Service پیام اطلاع‌رسانی می‌فرستد.

Checkout با Inventory ارتباط HTTP دارد؛ Payment رویداد PaymentConfirmed منتشر می‌کند؛ Order مصرف‌کننده است و Notification رویداد OrderPaid را می‌خواند. PSP خارج از کنترل تیم است. برای هر یال باید نوع قرارداد، مالک، Timeout، Retry، Idempotency و شواهد تست معلوم باشد.

فهرست مرزها پیش از نوشتن تست

مرز پروتکل مالک Provider ریسک غالب شاهد نزدیک
Checkout→Inventory HTTP Inventory Schema، وضعیت، Timeout Contract + Integration
Payment→PSP HTTPS شخص ثالث خطا، محدودیت، Sandbox drift Virtualization + Sandbox
Payment→Broker Event Payment Schema، تکرار، انتشار ناقص Message Contract + Outbox test
Broker→Order Event Order تکرار، ترتیب، Poison message Consumer Integration
Order→Notification Event Order تاخیر و شکست غیرحیاتی Contract + Async Journey

نقشهٔ شش‌لایه تست میکروسرویس

درصد ثابت برای تعداد تست‌ها وجود ندارد. معماری، ریسک و هزینهٔ بازخورد تعیین می‌کند هر لایه چقدر سهم داشته باشد. هدف، تکرار یک Assertion در شش لایه نیست؛ هر لایه باید سؤال متفاوتی را پاسخ دهد.

۱. Unit Test: منطق دامنه

تابع‌ها، Policyها، State Machine و تبدیل داده بدون شبکه و زیرساخت واقعی تست می‌شوند. محاسبه مبلغ، انتخاب Transition، اعتبارسنجی شناسه و سیاست Retry نمونه‌های مناسب‌اند. Unit Test سریع است، اما صحت Serialisation، Query، Broker و Policy استقرار را ثابت نمی‌کند.

۲. Component یا Service Test: یک سرویس از رابط عمومی

سرویس به‌صورت کامل از API یا Message Handler خودش فراخوانی می‌شود. زیرساخت تحت مالکیت سرویس، مانند PostgreSQL یا Broker، ترجیحاً نمونهٔ واقعی و موقت است؛ میکروسرویس‌های دیگر با Stub کنترل‌شده جایگزین می‌شوند. Component Test باید Boot، Middleware، Mapping، Persistence و Error Handling همان سرویس را ببیند.

مرز اصطلاحات در سازمان‌ها متفاوت است. بعضی تیم‌ها این سطح را Integration Test می‌نامند. نام مهم نیست؛ Artifact باید بگوید چه چیز واقعی و چه چیز شبیه‌سازی شده است.

۳. Contract Test: سازگاری Consumer و Provider

Contract Test ثابت می‌کند انتظاری که Consumer ثبت کرده، توسط Provider واقعی برآورده می‌شود. در Consumer-driven Contract، Consumer تعامل مورد نیاز را با Mock Server می‌آزماید و Contract منتشر می‌کند؛ Provider همان تعامل‌ها را روی نسخهٔ واقعی خود Verify می‌کند. فقط ساختن فایل OpenAPI یا JSON Schema، Provider Verification نیست.

۴. Integration Test: اتصال اجزای واقعی

دو یا چند جزء واقعی در مرز مشخص با هم آزموده می‌شوند: Service با Database، Handler با Broker، یا Consumer با Provider. این لایه باید رفتار فناوری واقعی را ببیند؛ مثلاً Isolation تراکنش، Serialization، Header، TLS، Queue policy یا IAM. برای تعریف دقیق‌تر دامنه، راهنمای تست یکپارچه‌سازی را ببینید.

۵. Journey یا End-to-End Test: نتیجهٔ کسب‌وکار

چند Journey حیاتی از ورودی عمومی تا اثر نهایی اجرا می‌شود؛ مثلاً «پرداخت موفق، سفارش را فقط یک بار Paid می‌کند». E2E شواهد ترکیب واقعی را می‌دهد، اما «بالاترین اطمینان» مطلق نیست: دامنهٔ ورودی کم، تشخیص علت کند و ماتریس نسخه محدود است. تعداد کم و هدفمند، بهتر از صدها Journey شکننده است.

۶. Production Verification: نسخهٔ واقعاً مستقر

Smoke، Synthetic، Canary، Metric و Trace بررسی می‌کنند Config، Routing، Certificate و Policy محیط درست‌اند. این لایه جای Pre-production Test را نمی‌گیرد و نباید دادهٔ واقعی مشتری را تخریب کند. Rollback و Stop Condition باید پیش از اجرا آماده باشند.

مرز Component، Integration و E2E را مستند کنید

ابهام سطح تست باعث دوباره‌کاری می‌شود. برای هر Suite یک «Runtime Manifest» کوتاه ثبت کنید:

Suite: payment-component
Real:
  - Payment Service image: payment:4.8.1
  - PostgreSQL 17 container
  - Kafka-compatible broker container
Simulated:
  - PSP HTTP endpoint
Not present:
  - Order Service
Entry point:
  - POST /payment-requests
Assertions:
  - HTTP response
  - payment row
  - outbox event
Owner:
  - Payment team

با این Manifest، عبارت «Integration سبز است» معنای قابل ممیزی پیدا می‌کند.

تست قرارداد HTTP چگونه اجرا می‌شود؟

Schema کافی نیست

OpenAPI می‌تواند Method، Path، Header، نوع فیلد و Status را توصیف کند. اما رفتارهایی مثل «رزرو ناموجود باید ۴۰۹ بدهد»، «شناسه تکراری همان نتیجه قبلی را برگرداند» و «کاربر فروشنده دیگر حق رزرو ندارد» به Example، Provider State و Assertion رفتاری نیاز دارند. برای پوشش کامل‌تر Request/Response، راهنمای تست API مکمل این بخش است.

جریان Consumer-driven Contract

  1. Consumer فقط تعامل‌هایی را ثبت می‌کند که واقعاً استفاده می‌کند.
  2. Contract با نسخه Consumer در Registry یا Broker منتشر می‌شود.
  3. Provider State دادهٔ لازم را به‌طور قطعی می‌سازد؛ مثلاً «SKU-۴۲ موجودی ندارد».
  4. Verifier درخواست را به Provider واقعی می‌فرستد.
  5. نتیجه با نسخه Provider ثبت می‌شود.
  6. Gate انتشار می‌پرسد آیا ترکیب نسخه‌های قابل استقرار با Consumerهای فعال سازگار است.

مستند رسمی Pact درباره Provider Verification نیز همین زنجیرهٔ Consumer test، Contract publication، Provider State و Verification را شرح می‌دهد.

چه چیزهایی بیرون Contract Test می‌ماند؟

  • Latency و رفتار زیر بار؛
  • DNS، Service Discovery و Certificate واقعی محیط؛
  • Policy شبکه و IAM؛
  • Race Condition و Consistency میان چند سرویس؛
  • درستی خود نیاز کسب‌وکار؛
  • وابستگی‌هایی که در Provider Verification Stub شده‌اند.

Contract Test یک Gate سریع سازگاری است، نه جایگزین Integration یا Journey.

تست قرارداد پیام و معماری Event-driven

برای پیام ناهمگام فقط Payload را بررسی نکنید. قرارداد باید Channel یا Topic، نام Event، نسخه، Key، Header، Correlation ID، زمان رخداد، Producer، Consumer و سیاست Compatibility را مشخص کند. ساختار رسمی AsyncAPI اجزایی مانند Channel، Operation، Message، Schema، Correlation ID و Security Scheme را مدل می‌کند.

نمونهٔ قرارداد PaymentConfirmed

{
  "eventId": "evt-01J...",
  "eventType": "PaymentConfirmed",
  "schemaVersion": 2,
  "occurredAt": "2026-08-06T18:42:11Z",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "paymentId": "pay-813",
  "orderId": "ord-819",
  "amountMinor": 12500000,
  "currency": "IRR"
}

amountMinor و currency ابهام ریال/تومان را حذف می‌کنند. eventId برای Deduplication است؛ paymentId شناسه دامنه است و نباید به‌جای Event ID استفاده شود.

تست‌های ضروری Consumer پیام

  • پیام معتبر نسخه فعلی مصرف می‌شود.
  • فیلد اختیاری جدید، Consumer قدیمی را نمی‌شکند.
  • فیلد الزامی گمشده به Retry بی‌پایان منجر نمی‌شود.
  • پیام تکراری، اثر کسب‌وکاری را دوباره اعمال نمی‌کند.
  • پیام دیررس یا خارج از ترتیب، State را عقب نمی‌برد.
  • Poison Message پس از سیاست مشخص به DLQ می‌رود.
  • Crash میان Side Effect و Ack باعث اثر تکراری نمی‌شود.
  • Correlation و Trace Context مطابق سیاست عبور می‌کند.

Idempotency، Retry و «دقیقاً یک بار»

در سامانه توزیع‌شده، تحویل تکراری و پاسخ گم‌شده طبیعی است. به‌جای تکیه بر برچسب Exactly-once، نتیجهٔ کسب‌وکاری را تست کنید:

  1. یک Idempotency-Key ثابت را دو بار بفرستید.
  2. بار دوم همان نتیجهٔ منطقی را بدون برداشت دوباره برگرداند.
  3. دو درخواست هم‌زمان با یک Key اجرا کنید.
  4. Crash را بعد از ثبت پرداخت و پیش از پاسخ شبیه‌سازی کنید.
  5. پس از Retry، Ledger فقط یک اثر مالی داشته باشد.

Oracle باید روی Ledger یا قید یکتای تحت مالکیت سرویس باشد، نه فقط Status Code. برای Event، جدول Inbox یا Dedup store و برای انتشار اتمیک، Outbox باید با Crash Pointهای مختلف آزموده شود.

تست Saga و Eventual Consistency

در جریان سفارش، تراکنش توزیع‌شده ممکن است با Saga جبران شود. تست صرفاً Happy Path کافی نیست. Stateهای میانی و Compensating Actionها را مدل کنید.

نقطه شکست انتظار فوری انتظار نهایی جبران
رزرو موجودی رد شد سفارش Pending نمی‌ماند Rejected پرداخت آغاز نشود
پرداخت Timeout شد وضعیت Unknown/Pending با Reconciliation تعیین شود آزادسازی زودهنگام ممنوع طبق Policy
پرداخت شکست قطعی Failed ثبت شود موجودی آزاد شود Release reservation
Event تأیید تکرار شد بار دوم شناسایی شود یک سفارش Paid بدون اثر مالی دوم
Notification شکست خورد سفارش Paid باقی بماند ارسال مجدد محدود عدم Rollback پرداخت

برای Assertion ناهمگام از Poll تا Deadline استفاده کنید، نه sleep(5000). شرط پایان باید نتیجهٔ دامنه باشد و Timeout تست، خطای قابل تشخیص تولید کند.

مدیریت وابستگی: Mock، Stub، Virtualization یا نمونه واقعی؟

گزینه کاربرد مناسب چه چیزی را ثابت نمی‌کند؟
Mock درون فرایند Unit و تعامل داخلی Serialization و شبکه
HTTP Stub Component و سناریوی خطا رفتار واقعی Provider
Service Virtualization شخص ثالث پرهزینه یا کمیاب Drift سرویس واقعی
Container واقعی Database، Broker و Cache Config مدیریت‌شده Cloud
Provider واقعی Integration محدود و هدفمند همه نسخه‌ها و خرابی‌ها
Sandbox شخص ثالث Protocol و Credential واقعی‌تر همسانی کامل Production

قاعده عملی: وابستگی تحت مالکیت سرویس مانند Database را در Component Test واقعی نگه دارید؛ سرویس‌های تیم دیگر را Stub کنید و سازگاری آن‌ها را با Contract Verify کنید؛ چند Integration محدود با نسخه واقعی نیز برای Config و Protocol نگه دارید.

مثال PSP ایرانی: سه سطح آزمون

سطح اول: Virtual PSP در Pull Request

پاسخ موفق، 4xx، 5xx، ۴۲۹، Timeout، Connection reset، JSON ناقص، امضای نامعتبر و Callback تکراری را قطعی تولید می‌کند. تست باید Secret ساختگی و دامنهٔ غیرواقعی داشته باشد.

سطح دوم: Sandbox در Merge یا Nightly

Handshake، Credential، Certificate، Redirect و Callback واقعی‌تر آزموده می‌شود. Sandbox ممکن است با Production تفاوت داشته باشد؛ نتیجه آن اثبات کامل سازگاری نیست.

سطح سوم: Canary کنترل‌شده

با حساب و مبلغ تست مجاز، مسیر کم‌ریسک اجرا و بلافاصله Reconcile می‌شود. مالک، بودجه، Cleanup، Alarm و Stop Condition باید مکتوب باشد. هیچ تستی نباید تراکنش مشتری را دست‌کاری کند.

Test Data در معماری Database-per-Service

هر سرویس باید دادهٔ تحت مالکیت خود را از رابط عمومی یا Fixture مجاز بسازد. Assertion مستقیم از Database سرویس دیگر، Coupling تست را زیاد و مالکیت را نقض می‌کند. برای طراحی چرخهٔ Seed، Isolation، Reset و Retention از راهنمای مدیریت داده تست استفاده کنید.

قواعد دادهٔ قابل تکرار

  • برای هر Run، شناسه و Namespace یکتا بسازید.
  • Clock را قابل کنترل و Timezone را صریح کنید.
  • دادهٔ مرجع Versioned باشد.
  • Cleanup فقط منابع همان Run را حذف کند.
  • برای جریان ناهمگام، Tombstone یا TTL داشته باشید.
  • از دادهٔ واقعی مشتری و شماره کارت/توکن Production استفاده نکنید.
  • در Parallel Run روی Key مشترک رقابت نکنید.
  • Seed شکست‌خورده، Suite را Fail-fast کند.

راهنمای AWS برای تست سامانه‌های توزیع‌شده نیز استفاده از شناسه یکتا، زیرساخت همراه برنامه و پاک‌سازی داده را برای جلوگیری از تداخل تست‌ها توصیه می‌کند؛ این اصول فقط مخصوص Serverless نیستند.

ماتریس خرابی را کنار ماتریس قابلیت بسازید

خرابی تزریق‌شده انتظار Client انتظار Service شاهد
Timeout پایین‌دست پاسخ محدود و قابل فهم Retry محدود با Jitter Span + Retry count
429 عدم تکرار کور احترام به Backoff/Retry-After Metric نرخ Retry
5xx موقت نتیجه موقت یا Pending Circuit/Retry طبق Policy State + Trace
Schema ناسازگار عدم فساد داده Reject یا DLQ Contract failure
Broker قطع درخواست طبق Policy پاسخ دهد Outbox باقی بماند Outbox row + recovery
DB Deadlock اثر تکراری نداشته باشد Transaction retry محدود Ledger invariant
پاسخ دیررس State عقب نرود نسخه/Sequence کنترل شود State transition log

Fault Injection در محیط ایزوله با مهندسی آشوب یکی نیست. Chaos Experiment فرضیه، Steady State، Blast Radius، Stop و Recovery مستقل می‌خواهد.

Observability بخشی از Testability است

بدون Correlation، شکست Journey فقط می‌گوید «سفارش نرسید». Trace باید مرز سرویس‌ها را عبور دهد تا معلوم شود Timeout در Inventory، Publish در Payment یا Consume در Order رخ داده است. مستند Context Propagation در OpenTelemetry توضیح می‌دهد Trace ID و Parent در مرز فرایندها منتقل می‌شوند و Trace، Log و Metric قابل هم‌بستگی می‌گردند.

شواهد استاندارد یک شکست

  • Test Run ID و Commit/Artifact version؛
  • Trace ID و Span شکست‌خورده؛
  • نسخه Consumer، Provider و Schema؛
  • Message ID، نه Payload حساس؛
  • State پیش و پس از عملیات؛
  • Retry count، Timeout و Error class؛
  • Environment و Config hash؛
  • زمان UTC و Clock source.

Baggage جای PII، Token یا Secret نیست. ورودی Trace از بیرون را اعتبارسنجی و هنگام خروج به سرویس غیرقابل اعتماد محدود کنید. برای روش تشخیص رخدادهای نامنظم، راهنمای بازتولید باگ متناوب مفید است.

تست Readiness، Liveness و Startup در Kubernetes

این سه Probe سه قرارداد مختلف‌اند. مستند رسمی Kubernetes می‌گوید Readiness تعیین می‌کند Pod ترافیک بگیرد، Liveness زمان Restart را مشخص می‌کند و Startup تا پایان راه‌اندازی، اجرای دو Probe دیگر را به تأخیر می‌اندازد.

سناریوهای تست Probe

  • برنامه در حال Startup است: نباید زودهنگام Liveness آن را Restart کند.
  • سرویس آماده پاسخ نیست: Readiness Fail و ترافیک قطع شود.
  • Process Deadlock دارد: Liveness پس از آستانه Fail شود.
  • وابستگی اختیاری Down است: Readiness کل سرویس را بی‌جهت حذف نکند.
  • وابستگی حیاتی Down است: رفتار Readiness مطابق قرارداد معماری باشد.
  • بار بالا است: Probe پرهزینه، آبشار Restart نسازد.
  • Shutdown آغاز شده: ترافیک جدید Drain و کار جاری طبق Grace Period تمام شود.

Liveness نباید با هر قطعی پایین‌دست Fail شود؛ وگرنه خرابی یک سرویس، بقیه Podها را نیز Restart می‌کند.

تست کارایی: جمع تأخیرها و Retry Storm

Benchmark یک Endpoint کافی نیست. در Journey، Tail Latency، Fan-out، Queue lag، Connection pool و Retry amplification باید دیده شوند. برای هر سرویس Budget تأخیر و برای کل Journey SLO تعیین کنید.

  • بار Open و Closed را آگاهانه انتخاب کنید.
  • p95 و p99 را کنار Throughput و Error rate بسنجید.
  • Retry را به‌عنوان درخواست جدید در Load Model حساب کنید.
  • Cold start، Cache miss و Rollout را نمونه‌برداری کنید.
  • Backpressure و Queue depth را زیر بار بررسی کنید.
  • Generator را از سامانهٔ هدف جدا و اعتبارسنجی کنید.

جزئیات مدل بار، Threshold و Stop Condition در برنامه تست عملکرد آمده است.

تست امنیت سرویس‌به‌سرویس

اعتماد به شبکه داخلی، کنترل امنیتی نیست. Gateway فقط لایه اول است؛ هر سرویس باید هویت Caller و مجوز Resource/Action را مطابق معماری بررسی کند.

ریسک تست نمونه انتظار
Gateway bypass فراخوانی مستقیم سرویس بدون هویت معتبر رد درخواست
Token با Audience غلط توکن سرویس دیگر رد قبل از منطق دامنه
مجوز بین مالک‌ها فروشنده A روی سفارش B عدم افشای داده و رد
Replay تکرار درخواست امضاشده مطابق Nonce/Idempotency policy
Secret rotation هم‌زیستی کلید قدیم/جدید Rollout بدون قطعی و پایان Grace
Log leakage خطای پایین‌دست حاوی Token Redaction قطعی

راهنمای امنیت میکروسرویس OWASP بر احراز هویت سرویس‌به‌سرویس، انتقال امن هویت خارجی و اعمال مجوز در چند لایه تأکید می‌کند. برای برنامه کامل‌تر، راهنمای تست امنیت نرم‌افزار را ببینید.

طراحی Pipeline تست میکروسرویس

هر Lane باید زمان بازخورد، دامنه، مالک شکست و Gate مشخص داشته باشد. برای معماری عمومی Laneها می‌توانند چنین باشند:

Lane محرک آزمون‌ها بودجه زمانی Gate
PR هر Commit Unit، Component، Consumer Contract، Static چند دقیقه Block merge
Provider تغییر API/Event Provider Verification همه Consumer فعال کوتاه Block artifact
Merge Main Integration با DB/Broker/Identity متوسط Block promotion
Nightly زمان‌بندی ماتریس نسخه، Sandbox، Failure، Migration طولانی‌تر Quarantine release
Pre-release Candidate Journey، Performance، Security، Resilience ریسک‌محور Release decision
Post-deploy Canary Smoke، Synthetic، SLO، Trace دقایق اول Promote/Rollback

اصول Lane، Quality Gate و Promotion بدون Rebuild در راهنمای Continuous Testing در CI/CD تشریح شده است.

Gate سازگاری نسخه

Provider فقط نباید Contract شاخه Main را Verify کند. باید با Consumerهایی که اکنون در Production یا محیط هدف فعال‌اند سازگار باشد. رجیستری Contract باید Deployment، Version و Environment را بداند.

  • تغییر Additive معمولاً امن‌تر است، اما Semantic را نیز بررسی کنید.
  • فیلد جدید را ابتدا Optional منتشر کنید.
  • Consumerها را مهاجرت و Telemetry مصرف نسخه قدیم را مشاهده کنید.
  • پس از خروج آخرین Consumer، فیلد یا Endpoint قدیم را حذف کنید.
  • Rollback Provider باید با Consumer تازه هم سازگار باشد.
  • Migration دیتابیس از الگوی Expand→Migrate→Contract پیروی کند.

راهنمای DevOps در AWS Well-Architected نیز Contract Versioned و اجرای آزمون سازگاری در Pipeline هر دو سمت را توصیه می‌کند.

محیط تست: Production-like یعنی چه؟

کپی کامل Production معمولاً گران و ناپایدار است. «مشابه تولید» باید به ابعاد ریسک شکسته شود:

  • نسخه Runtime، Database و Broker؛
  • Topology، Network policy و TLS؛
  • IAM و Service account؛
  • Schema و Migration state؛
  • حجم و شکل داده، نه داده واقعی مشتری؛
  • Config، Feature flag و Locale؛
  • Clock، Timezone و Certificate expiry؛
  • Resource limit و Autoscaling policy.

برای هر Suite فقط ابعادی را واقعی کنید که Assertion به آن وابسته است. Environment مشترک بدون Namespace و مالک Cleanup منبع اصلی Flakiness است.

ابزار را براساس خلأ شاهد انتخاب کنید

لیست ثابت «بهترین ابزارها» عمر کوتاهی دارد. ابتدا بپرسید کدام شاهد کم است:

  • برای اجرای Dependency واقعی موقت: Container orchestration و Test fixture؛
  • برای HTTP virtualization: Stub server با Fault scripting؛
  • برای Contract: Registry/Broker، Provider State و deploy compatibility؛
  • برای Event: Schema registry، Async contract و message harness؛
  • برای Journey: API/UI runner با Polling و Trace capture؛
  • برای Fault: proxy یا platform fault injection با Blast radius؛
  • برای Observability: trace/log/metric correlation؛
  • برای داده: namespace، seed و cleanup automation.

در PoC، زمان تشخیص خطا، پشتیبانی پروتکل، اجرای CI، امنیت Artifact، خروجی استاندارد و هزینه نگهداری را اندازه بگیرید. ابزار نباید معماری تست را دیکته کند.

متریک‌های سالم برای استراتژی

  • Contract escape: ناسازگاری‌هایی که پس از Gate کشف شدند.
  • Time to signal: p50/p95 زمان بازخورد هر Lane.
  • Time to diagnose: زمان از Fail تا تعیین سرویس/مرز مسئول.
  • Flake rate: Fail بدون تغییر محصول و Pass در Re-run.
  • Critical journey coverage: ریسک‌های کسب‌وکاری دارای شاهد، نه تعداد تست.
  • Async invariant coverage: تکرار، ترتیب، تأخیر، DLQ و جبران آزموده‌شده.
  • Version compatibility coverage: ترکیب نسخه‌های فعال Verify‌شده.
  • Production escape by boundary: مرزی که شاهد کافی نداشت.

تعداد Mock، تعداد E2E یا درصد Automation به‌تنهایی Outcome نیست. متریک باید تصمیم ایجاد کند؛ مثلاً افزایش Flake در Journey به کاهش دامنه و انتقال Assertion به Component منجر شود.

برنامه ۳۰ روزه پیاده‌سازی

هفته اول: نقشه و Baseline

  • پنج Journey حیاتی و همه یال‌های سرویس را رسم کنید.
  • برای هر یال Protocol، Owner، Version و Failure policy بنویسید.
  • Suiteهای فعلی را با Runtime Manifest برچسب بزنید.
  • زمان، Flake و Escape فعلی را اندازه بگیرید.

هفته دوم: Component و Contract

  • یک سرویس پرریسک را از رابط عمومی با DB واقعی موقت تست کنید.
  • برای یک Consumer/Provider، Contract و Provider State بسازید.
  • Contract Registry و Gate نسخه را در CI وصل کنید.
  • Schema پیام مهم را Versioned کنید.

هفته سوم: Async و Failure

  • Duplicate، Out-of-order، Poison و Crash-before-Ack را تست کنید.
  • Outbox/Inbox و Idempotency را با Concurrent request بررسی کنید.
  • Trace ID را از HTTP تا Event عبور دهید.
  • ماتریس Timeout/۴۲۹/5xx/Broker down را اجرا کنید.

هفته چهارم: Journey و Release Gate

  • دو Journey حیاتی را با Polling و Trace بازنویسی کنید.
  • Probe و Shutdown را در Rollout تست کنید.
  • Smoke/Canary با Rollback condition بسازید.
  • نتایج را در جلسه مالک سرویس و QA بازبینی کنید.

چک‌لیست نهایی تست میکروسرویس‌ها

  • نقشه Service/Boundary/Owner به‌روز است.
  • هر Suite می‌گوید چه Dependency واقعی یا شبیه‌سازی شده است.
  • Contract هم در Consumer و هم Provider Verify می‌شود.
  • نسخه‌های فعال Consumer در Gate انتشار دیده می‌شوند.
  • API همگام و Event ناهمگام هر دو قرارداد ماشین‌خوان دارند.
  • Duplicate، Retry، Timeout، Out-of-order و DLQ آزموده شده‌اند.
  • Idempotency با هم‌زمانی و Crash Point بررسی شده است.
  • Test Data با Run ID یکتا ایزوله و پاک می‌شود.
  • Assertion بین‌سرویسی به DB داخلی سرویس دیگر متکی نیست.
  • Trace/Log/Metric با Correlation قابل اتصال‌اند.
  • PII، Token و Secret در Trace یا Artifact نیست.
  • Readiness، Liveness و Startup جداگانه تست شده‌اند.
  • Performance شامل Tail latency، Queue lag و Retry amplification است.
  • Authorization در Gateway و Service boundary آزموده می‌شود.
  • Journeyهای E2E کم، حیاتی و قابل تشخیص‌اند.
  • Canary دارای مالک، Alarm، Stop و Rollback است.

منابع معتبر

جمع‌بندی

استراتژی تست میکروسرویس از «هرم تست با چند درصد ثابت» شروع نمی‌شود؛ از مرزها و پیامد شکست آغاز می‌شود. منطق داخل سرویس در Unit، کل سرویس در Component، سازگاری نسخه در Contract، زیرساخت واقعی در Integration، نتیجه کسب‌وکار در Journey و پیکربندی واقعی در Production Verification شاهد می‌گیرد.

برای جریان سفارش و پرداخت، بیشترین ارزش معمولاً از Contract versioned، Idempotency، تست پیام تکراری/دیررس، Outbox/Inbox، Trace بین‌سرویسی و چند Journey قابل تشخیص می‌آید. وقتی هر شکست به Owner، Boundary و Evidence متصل باشد، معماری توزیع‌شده از یک محیط مبهم و شکننده به سامانه‌ای قابل آزمون و قابل انتشار تبدیل می‌شود.

سوالات متداول تست میکروسرویس‌ها

۱. تفاوت Component Test و Integration Test در میکروسرویس چیست؟

در قرارداد این مقاله، Component Test یک سرویس کامل را از رابط عمومی می‌آزماید؛ زیرساخت تحت مالکیت آن می‌تواند واقعی باشد و سرویس‌های دیگر Stub شوند. Integration Test اتصال دو یا چند جزء واقعی، مانند Service و Broker یا Consumer و Provider را بررسی می‌کند. چون نام‌گذاری تیم‌ها متفاوت است، Runtime Manifest از خود نام مهم‌تر است.

۲. آیا Contract Test جای E2E را می‌گیرد؟

خیر. Contract سازگاری انتظار Consumer با Provider را سریع می‌سنجد، اما Config محیط، IAM، Routing، Eventual Consistency و نتیجه کل Journey را ثابت نمی‌کند. E2E نیز همه ترکیب نسخه و خطا را پوشش نمی‌دهد. دو لایه مکمل‌اند.

۳. برای Eventهای میکروسرویس چه چیزهایی را تست کنیم؟

Schema و Version، Header و Correlation، پیام تکراری، ترتیب متفاوت، تأخیر، فیلد ناشناخته، Poison Message، Retry/DLQ، Crash پیش از Ack، Idempotency و اثر نهایی دامنه را تست کنید. فقط معتبر بودن JSON کافی نیست.

۴. چند تست End-to-End لازم است؟

عدد عمومی وجود ندارد. برای Journeyهایی E2E بنویسید که ریسک کسب‌وکاری بالا دارند و با لایه پایین‌تر قابل اثبات نیستند. هر Journey باید Owner، داده ایزوله، Polling تا Deadline، Trace و دلیل شکست قابل تشخیص داشته باشد.

۵. چگونه Flaky Test میکروسرویس را کم کنیم؟

داده و Namespace هر Run را یکتا کنید، Sleep ثابت را با Polling شرط‌محور جایگزین کنید، وابستگی نامرتبط را Stub کنید، Clock را کنترل کنید، Contract را از Journey جدا کنید و Trace را ذخیره کنید. Re-run خودکار بدون Root-cause فقط نرخ واقعی Flake را پنهان می‌کند.

دیدگاهتان را بنویسید