یک تست 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های معماری را به نقاط آزمون تبدیل کرد.
مدل نمونه: سفارش و پرداخت در فروشگاه ایرانی
سناریوی نمونه پنج جزء دارد:
- Checkout Service درخواست خرید را دریافت میکند.
- Inventory Service موجودی را رزرو میکند.
- Payment Service درخواست پرداخت را به PSP میفرستد و Callback را دریافت میکند.
- Order Service وضعیت سفارش را از Eventها میسازد.
- 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
- Consumer فقط تعاملهایی را ثبت میکند که واقعاً استفاده میکند.
- Contract با نسخه Consumer در Registry یا Broker منتشر میشود.
- Provider State دادهٔ لازم را بهطور قطعی میسازد؛ مثلاً «SKU-۴۲ موجودی ندارد».
- Verifier درخواست را به Provider واقعی میفرستد.
- نتیجه با نسخه Provider ثبت میشود.
- 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، نتیجهٔ کسبوکاری را تست کنید:
- یک
Idempotency-Keyثابت را دو بار بفرستید. - بار دوم همان نتیجهٔ منطقی را بدون برداشت دوباره برگرداند.
- دو درخواست همزمان با یک Key اجرا کنید.
- Crash را بعد از ثبت پرداخت و پیش از پاسخ شبیهسازی کنید.
- پس از 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 است.
منابع معتبر
- AWS Prescriptive Guidance: Testing at architecture boundaries؛ مرز، Contract، Test harness، Isolation و Cleanup.
- Pact Specification؛ قرارداد Consumer-driven و سازگاری Matching میان پیادهسازیها.
- AsyncAPI Payload Schema؛ قالب و Schema پیامهای Event-driven.
- OpenTelemetry Propagators API؛ انتقال Context میان مرزهای فرایند.
جمعبندی
استراتژی تست میکروسرویس از «هرم تست با چند درصد ثابت» شروع نمیشود؛ از مرزها و پیامد شکست آغاز میشود. منطق داخل سرویس در 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 را پنهان میکند.

