مصطفی جعفرزاده
مصطفی جعفرزاده
خواندن ۱۸ دقیقه·۲ ماه پیش

تسلط بر چالش‌های TDD در پروژه‌های B2B: تکنیک‌های حرفه‌ای از پارامتری‌شده تا Spy و Mock


مقدمه

همان طور که میدانید Test-Driven Development (TDD) در پروژههای B2B میتواند تأثیر زیادی در کیفیت و قابلیت نگهداری سیستمهای پیچیده داشته باشد. در محیط B2B، سرویسها معمولاً با مشتریان شرکتی کار میکنند که نیازهای خاص و انتظارات بالایی دارند. TDD میتواند در ساخت نرمافزارهایی که از قابلیت اطمینان، مقیاسپذیری، و کیفیت بالاتری برخوردارند، بسیار مفید باشد.


پیادهسازی TDD در پروژههای B2B با چالشهای متعددی همراه است. این مقاله با هدف ارائه یک تحلیل کامل و فوق حرفهای، به بررسی چالشهای مختلف TDD در پروژههای B2B میپردازد و راهکارهای مؤثری برای مقابله با این چالشها، بهویژه با استفاده از تستهای یکپارچه و قراردادی، تستهای پارامتریشده، و تستهای تغییر حالت و انتقال، ارائه میدهد.


نکته: در توسعه این مقاله از هوش مصنوعی استفاده شده است


۱. چالشهای وابستگیهای پیچیده و وابستگیهای خارجی

۱.۱ وابستگیهای بین ماژولها

پروژههای B2B به طور معمول به صورت ماژولار طراحی میشوند و هر ماژول به دیگری وابسته است. این وابستگیها میتوانند تست واحد هر ماژول را دشوار کنند، بهویژه هنگامی که ماژولها نیاز به دادههای یکدیگر دارند.


- مشکل اصلی: وابستگی بین ماژولها باعث میشود بدون شبیهسازی رفتارهای وابسته نتوان بهطور مستقل تست انجام داد. برای مثال، اگر ماژول مدیریت سفارش نیاز به اطلاعات موجودی از ماژول مدیریت انبار داشته باشد، تست مدیریت سفارش به تنهایی دشوار خواهد بود.

- راهحل و مثال: استفاده از Mock و Stub برای شبیهسازی رفتار وابسته. به عنوان مثال، در یک سیستم مدیریت زنجیره تأمین، از Mock برای شبیهسازی رفتار ماژول مدیریت انبار استفاده کنید تا تست مدیریت سفارش به درستی انجام شود.


۱.۲ وابستگی به سرویسهای خارجی

در بسیاری از پروژههای B2B، وابستگی به سرویسهای خارجی مانند APIهای پرداخت، سیستمهای حملونقل، و سرویسهای تأمین وجود دارد. این وابستگیها ممکن است مانع اجرای تستهای مکرر شوند.


- مشکل اصلی: عدم دسترسی همیشگی به این سرویسها یا کندی آنها میتواند زمان تست را افزایش دهد یا نتایج تستها را غیرقابل پیشبینی کند.

- راهحل و مثال: فرض کنید یک پلتفرم B2B مانند Amazon Business که خدمات خرید و فروش کالا به شرکتها ارائه میدهد، نیاز به ارتباط با APIهای تأمینکنندگان دارد. برای تستهای TDD، از Stub استفاده میشود تا رفتار APIهای تأمینکننده شبیهسازی شود و وابستگی به این سرویسها حذف شود.


۲. پیچیدگی در مدیریت دادههای بزرگ و متنوع


۲.۱ مقیاسپذیری و حجم بالای دادهها

پروژههای B2B اغلب با حجم بالایی از دادهها سر و کار دارند و تست TDD که باید بر اساس دادههای واقعی اجرا شود، ممکن است چالشبرانگیز باشد.


- مشکل اصلی: تستهایی که بر روی دادههای واقعی انجام میشوند، به دلیل حجم بالای دادهها، زمانبر بوده و اجرای مداوم آنها دشوار است.

- راهحل و مثال: استفاده از نمونههای داده کوچکتر برای تستهای واحد. به عنوان مثال، در یک سیستم ERP، به جای استفاده از تمام دادههای موجودی برای تست، از نمونههای کوچک داده استفاده میشود.


۲.۲ ناسازگاری دادهها

دادهها در سیستمهای B2B ممکن است به دلیل تعامل با منابع مختلف ناسازگار باشند، که این موضوع میتواند منجر به خطاهای غیرواقعی در تستها شود.


- مشکل اصلی: هماهنگسازی بین دادههای مختلف باعث پیچیدگی در تستها میشود و گاهی نمیتوان تمامی سناریوها را به درستی شبیهسازی کرد.

- راهحل و مثال: در یک سیستم تحلیل دادههای تبلیغاتی، میتوان از فایلهای پیکربندی Stub برای تعریف دادههای استاندارد برای تست استفاده کرد تا مشکلات ناسازگاری دادهها کاهش یابد.

۳. مدیریت پیچیدگیها و شرایط غیرمنتظره


۳.۱ مدیریت شرایط ناهنجار و خطاهای غیرمنتظره

در پروژههای B2B، احتمال وقوع خطاهای پیچیده و غیرمنتظره، مانند خطاهای شبکه یا ناهماهنگی دادهها، وجود دارد. تست TDD باید به گونهای طراحی شود که این شرایط را شبیهسازی کند.


- مشکل اصلی: شبیهسازی شرایط غیرمنتظره مانند خطاهای شبکه یا پاسخهای ناقص سرویسها میتواند بسیار پیچیده باشد.

- راهحل و مثال: استفاده از Mock برای شبیهسازی خطاهای غیرمنتظره در زمان تست. به عنوان مثال، در یک سیستم بانکی B2B، با استفاده از Mock میتوان خطاهای ارتباط با سرویس پرداخت را شبیهسازی کرد.


۳.۲ هماهنگی تستهای موازی و همزمانی

پروژههای B2B شامل پردازشهای موازی هستند و تست این پردازشها به دلیل همزمانی و ترتیبهای مختلف اجرا، بسیار پیچیده است.


-مشکل اصلی: اجرای پردازشهای موازی باعث میشود تستها به نتایج متفاوتی برسند، زیرا ترتیب اجرا میتواند متفاوت باشد.

- راهحل و مثال: در یک سیستم مدیریت موجودی آنلاین، از Mock و Spy برای بررسی تعداد فراخوانیها و وضعیت همزمانی استفاده میشود تا اطمینان حاصل شود که هیچ تداخلی بین درخواستهای همزمان وجود ندارد.


۴. هزینه و زمان بالای توسعه TDD در محیط B2B


۴.۱ هزینه نگهداری تستها با تغییرات مداوم

پروژههای B2B تحت تغییرات مداوم قرار دارند و هر گونه تغییر در سیستم باید در تستهای TDD نیز منعکس شود.


- مشکل اصلی: تغییر مداوم نیازهای پروژه منجر به تغییرات در تستها شده و هزینه نگهداری این تستها را افزایش میدهد.

- راهحل و مثال: استفاده از تستهای انعطافپذیر که با تغییرات ماژولهای مختلف به راحتی قابل تنظیم باشند. در یک سیستم مدیریت قراردادهای B2B، تستها باید بهگونهای طراحی شوند که تغییرات در منطق قراردادها به راحتی اعمال شوند.


۴.۲ زمانبر بودن پیادهسازی اولیه TDD

پیادهسازی TDD در ابتدا زمانبرتر از توسعه بدون تست است، که این زمان اضافی ممکن است برای پروژههای B2B که زمان تحویل حیاتی است، چالش ایجاد کند.


- مشکل اصلی: زمان طولانی برای نوشتن و تطبیق تستها باعث میشود برخی تیمها به سمت حذف تستها پیش بروند.

- راهحل و مثال: در یک پلتفرم بازاریابی دیجیتال، میتوان از تستهای ساده و اولیه استفاده کرد و به مرور زمان تستهای جامعتر را اضافه نمود تا فشار کمتری بر تیم وارد شود.


۵. چالشهای هماهنگی بین تیمهای توسعه و تست در پروژههای B2B


۵.۱ عدم هماهنگی بین تیمهای مختلف

پروژههای B2B معمولاً تیمهای متعددی دارند که بر روی بخشهای مختلف کار میکنند، و عدم هماهنگی بین این تیمها میتواند باعث ناسازگاری در تستها شود.


-مشکل اصلی: نبود هماهنگی باعث میشود که تستهایی که یک تیم نوشته است، با تغییرات تیم دیگر ناسازگار باشند.

-راهحل و مثال: استفاده از تستهای قراردادی (Contract Tests) بین ماژولهای مختلف برای تعریف نحوه تعامل و تضمین اینکه تغییرات تأثیر منفی ندارند. در یک سیستم مدیریت زنجیره تأمین، تستهای قراردادی تضمین میکنند که تغییرات در ماژول مدیریت انبار تأثیر منفی بر مدیریت سفارش نداشته باشد.


۵.۲ چالشهای هماهنگی تستهای یکپارچه (Integration Testing)

تستهای یکپارچه در پروژههای B2B برای اطمینان از عملکرد هماهنگ ماژولها ضروری هستند. با این حال، پیادهسازی TDD برای این تستها چالشبرانگیز است.


- مشکل اصلی: تنظیم و اجرای تستهای یک


پارچه به دلیل وابستگیهای پیچیده، زمانبر است و یافتن مشکلات بین چند ماژول بسیار دشوارتر از تستهای واحد است.

- راهحل و مثال: استفاده از پیکربندیهای Mock برای تعاملات بین ماژولها به طوری که بتوان هر ماژول را جداگانه تست کرد و سپس در تستهای یکپارچه، صحت تعاملات بررسی شود.


۶. شبیهسازی و پوشش سناریوهای واقعی و پیچیده


۶.۱ پوشش تمامی حالات و سناریوهای ممکن

در پروژههای B2B، باید اطمینان حاصل کرد که تمامی سناریوهای ممکن شامل شرایط عادی و غیرعادی پوشش داده شده است.


- مشکل اصلی: به دلیل پیچیدگی سیستمهای B2B، تعداد زیادی سناریو وجود دارد که ممکن است فراموش شود یا نادیده گرفته شود.

-راهحل و مثال: در یک پلتفرم تبلیغات دیجیتال، سناریوهایی مانند کمبود بودجه تبلیغات، عدم دریافت دادههای کاربری، یا تغییرات قوانین باید پوشش داده شود و از Mock و Stub برای شبیهسازی این سناریوها استفاده شود.


۶.۲ شبیهسازی تعاملات طولانی و چند مرحلهای

در پروژههای B2B، تعاملات چندمرحلهای بین ماژولها پیچیدگی زیادی دارند و تست این تعاملات در TDD دشوار است.


-مشکل اصلی: ممکن است چندین مرحله در طول یک فرآیند وجود داشته باشد که هر کدام به مرحله قبلی وابسته است، و تست کردن تمامی این مراحل به زمان و منابع زیادی نیاز دارد.

- راهحل و مثال: استفاده از تستهای حالت (State Testing) و تستهای انتقال بین حالتها (Transition Testing) با استفاده از Stub و Mock برای شبیهسازی هر مرحله از فرآیند. به عنوان مثال، در یک سیستم مدیریت زنجیره تأمین، از Stub و Mock برای بررسی تعاملات هر مرحله از فرآیند ارسال کالا استفاده میشود.


۷. تغییر نیازهای کسبوکار و تطبیق تستها با نیازهای جدید


۷.۱ تغییرات سریع نیازهای تجاری

پروژههای B2B اغلب بر اساس نیازهای مشتریان توسعه مییابند که ممکن است به سرعت تغییر کنند. تطبیق تستهای TDD با این تغییرات یکی از چالشهای عمده است.


- مشکل اصلی: تغییرات سریع نیازهای پروژه باعث میشود تستها بهسرعت تغییر کنند و هزینه نگهداری آنها افزایش یابد.

- راهحل و مثال: طراحی تستهای انعطافپذیر که به راحتی با تغییرات جدید تطبیق پیدا کنند، مانند استفاده از Test Parameterization. در یک پلتفرم مدیریت قراردادهای B2B، تستهای پارامتریشده میتوانند با تغییر یک پارامتر به سرعت با شرایط جدید تطبیق یابند.


۷.۲ تأثیر تغییرات در یک ماژول بر سایر ماژولها

در پروژههای B2B، تغییر در یک ماژول ممکن است بر سایر ماژولها تأثیرگذار باشد. اطمینان از اینکه تستهای TDD تمامی این تأثیرات را پوشش میدهند، ضروری است.


- مشکل اصلی: نبود هماهنگی در تغییرات بین تیمها ممکن است منجر به شکست تستهای واحد شود.

-راهحل و مثال: استفاده از تستهای قراردادی (Contract Tests) که مشخص کنند چگونه تغییرات در یک ماژول بر سایر ماژولها تأثیر میگذارد. در یک سیستم مدیریت منابع انسانی (HRM)، تغییرات در ماژول محاسبه حقوق میتواند تأثیر زیادی بر سایر بخشها مانند مالیات یا پرداختهای مزایا داشته باشد.


۸. چالشهای مدیریت هزینه و زمان در پیادهسازی TDD


۸.۱ هزینه و زمان آموزش TDD در پروژههای بزرگ

میدانیمTDD به تخصص نیاز دارد و در پروژههای B2B که معمولاً تیمهای بزرگ و متعددی در آن فعالیت میکنند، پیادهسازی آن چالشبرانگیز است.


- مشکل اصلی: هزینههای آموزش و ایجاد فرهنگ TDD در تیمهای بزرگ ممکن است زیاد باشد و پیادهسازی آن زمانبر باشد.

- راهحل و مثال: در پروژههای توسعه پلتفرم SaaS، برنامههای آموزشی برای معرفی TDD و نحوه استفاده از Stub و Mock میتواند توسعهدهندگان را به استفاده از این روش ترغیب کند.


۸.۲ زمانبری پیادهسازی و نگهداری تستها

میدانیم TDD زمانبرتر از توسعه بدون تست است و در پروژههای B2B که سرعت تحویل اهمیت دارد، این موضوع چالشبرانگیز است.


- مشکل اصلی: زمان طولانی برای نگهداری تستها باعث میشود برخی تیمها به سمت حذف تستها پیش بروند.

- راهحل و مثال: استفاده از تستهای خودکار و CI/CD برای کاهش زمان اجرای تستها. به عنوان مثال، در یک پلتفرم بازاریابی دیجیتال B2B، استفاده از CI/CD میتواند زمان مورد نیاز برای اجرای تستهای TDD را کاهش دهد.


۹. ترکیب Stub و Mock برای TDD در پروژههای B2B


برای استفاده حرفهای از Stub و Mock در TDD پروژههای B2B، ترکیب مناسب آنها میتواند باعث ایجاد یک محیط تست کارآمد و واقعیتر شود. Stub و Mock هر کدام مزایا و کاربردهای خاص خود را دارند و در محیطهای پیچیده B2B، استفاده ترکیبی از آنها اهمیت زیادی دارد.


۹.۱ تفاوت Stub و Mock و نقش آنها در TDD

-تعریف Stub: یک شیء شبیهسازی شده است که به جای یک وابستگی واقعی قرار میگیرد و دادههای از پیش تعیینشده را برمیگرداند.

-تعریف Mock: یک شیء شبیهسازی شده است که علاوه بر ارائه داده، رفتار فراخوانی را ثبت و بررسی میکند.


۹.۲ موارد استفاده از Stub و Mock در پروژههای B2B

۹.۲.۱ تعامل با سیستمهای خارجی (APIهای پرداخت، سرویسهای تأمین)

- همیشه Stub برای شبیهسازی پاسخهای ثابت از سیستمهای خارجی و Mock برای بررسی نحوه فراخوانی و مدیریت خطاها.

۹.۲.۲ شبیهسازی رفتار پایگاه داده و تعاملات پیچیده دادهها

-همیشه Stub برای شبیهسازی نتایج دیتابیس و Mock برای بررسی تعداد دفعات فراخوانی و صحت کوئریها.


۹.۲.۳ شبیهسازی و تست وابستگیهای ماژولار و پیچیده

- همیشه Stub برای جداسازی ماژولها و Mock برای بررسی نحوه ارتباط بین ماژولها.


۹.۲.۴ شبیهسازی و کنترل شرایط غیرمنتظره

- اکثر اوقات Stub برای شبیهسازی پاسخهای غیرمنتظره و Mock برای بررسی رفتار سیستم در مواجهه با خطاها.


۹.۳ ترکیب حرفهای Stub و Mock برای TDD در پروژههای B2B

الگوی Stub برای داده و Mock برای منطق

این الگو برای تأمین دادههای ثابت با Stub و بررسی منطق فراخوانیها با Mock استفاده میشود.


الگوی Mock برای سرویسهای اصلی و Stub برای وابستگیها

این الگو برای تست سرویسهای اصلی با Mock و وابستگیها با Stub به کار میرود.


استفاده همزمان از Stub و Mock برای بررسی فرآیندهای پیچیده

در فرآیندهای چند مرحلهای، از Stub برای سادهسازی دادهها و از Mock برای بررسی هر مرحله استفاده میشود.

۱۰. تستهای پیشرفته در پروژههای B2B: ترکیب تست پارامتریشده، تغییر حالت، انتقال، و ترکیب Mock و Spy


در این بخش، به بررسی تستهای پیشرفته در پروژههای B2B میپردازیم که شامل تستهای پارامتریشده، تغییر حالت، انتقال و ترکیب Mock و Spy میباشد. این تستها میتوانند به توسعهدهندگان کمک کنند تا سیستمهای پیچیده B2B را با کیفیت بالاتر و خطاهای کمتر توسعه دهند.


۱۰.۱ تستهای یکپارچه و قراردادی (Contract Tests)

- تستهای یکپارچه برای اطمینان از عملکرد صحیح سیستمها یا بخشهای مختلف در کنار یکدیگر به کار میروند. برای B2B، این تستها برای بررسی تعامل بین میکروسرویسها استفاده میشوند.

- تستهای قراردادی تضمین میکنند که یک سرویس، قراردادی را که به سرویس دیگری وعده داده، رعایت میکند. این تستها مخصوصاً برای سیستمهای B2B که به میکروسرویسها یا APIها وابسته هستند، بسیار حیاتی هستند.

- برای پیادهسازی تستهای قراردادی در Node.js میتوانید از کتابخانههایی مثل [Pact](https://docs.pact.io/) استفاده کنید که به کمک آنها میتوان قرارداد بین سرویسهای مصرفکننده و ارائهدهنده را تعریف و اعتبارسنجی کرد.


مثال:

فرض کنید دو سرویس دارید: یکی برای مشتری (Consumer) و دیگری برای محصول (Provider). در TDD شما اول تست قراردادی برای مشتری مینویسید که تضمین کند این سرویس باید یک محصول را با ساختار مشخصی دریافت کند.


const { Pact } = require('@pact-foundation/pact');
const provider = new Pact({
consumer: 'CustomerService',
provider: 'ProductService',
port: 1234,
});
describe('Contract Test', () => {
beforeAll(() => provider.setup());
afterAll(() => provider.finalize());
it('should return a product', async () => {
await provider.addInteraction({
state: 'a product exists',
uponReceiving: 'a request for a product',
withRequest: {
method: 'GET',
path: '/product/1',
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: {
id: 1,
name: 'Product A',
price: 100,
},
},
});
// Test consumer code against provider
// ...
});
});


۱۰.۲ تست پارامتریشده (Test Parameterization)

- تست پارامتریشده برای آزمایش کد با ورودیهای متفاوت به کار میرود و به شما این امکان را میدهد که با کاهش کد تکراری و بررسی همه سناریوها از پوشش تستی بیشتری برخوردار شوید.

- میتوانید از فریمورکهایی مثل Jest برای پیادهسازی تستهای پارامتریشده استفاده کنید.


مثال: ثبت سفارش در سیستم B2B

فرض کنید یک سیستم B2B دارید که محصولات را به فروشگاههای دیگر عرضه میکند و باید عملیات ثبت سفارشها را برای مشتریان مختلف بررسی کنید. برای تست پارامتریشده این API با انواع ورودیها (مثلاً محصولات مختلف، مقادیر متفاوت سفارش، وضعیتهای مختلف کاربری) میتوانید از یک تست پارامتریشده استفاده کنید.


const request = require('supertest');
const app = require('../app'); // فرض کنید این فایل اپلیکیشن شما را اجرا میکند
describe('B2B Order API Parameterized Tests', () => {
const orderData = [
{ productId: 'P001', quantity: 10, expectedStatus: 200 },
{ productId: 'P002', quantity: 5, expectedStatus: 200 },
{ productId: 'P003', quantity: 0, expectedStatus: 400 }, // تعداد نامعتبر
{ productId: 'INVALID_ID', quantity: 3, expectedStatus: 404 }, // شناسه نامعتبر
];
test.each(orderData)(
'should handle order for productId: %s and quantity: %i',
async ({ productId, quantity, expectedStatus }) => {
const response = await request(app)
.post('/api/orders')
.send({ productId, quantity });
expect(response.status).toBe(expectedStatus);
}
);
});


توضیحات:

- request: برای ارسال درخواست HTTP به API از supertest استفاده میکنیم.

- orderData: شامل یک آرایه از دادههای ورودی مختلف است که میخواهیم تست کنیم. هر شیء شامل productId، quantity، و expectedStatus است.

- test.each(orderData): به ما اجازه میدهد که برای هر ورودی در orderData یک تست جداگانه اجرا کنیم.

- expectedStatus: وضعیت مورد انتظار برای پاسخ API را مشخص میکند (مثلاً 200 برای موفقیت یا 400 برای خطا).


۱۰.۳ تستهای تغییر حالت (State Testing)

- تستهای تغییر حالت زمانی به کار میروند که رفتار سیستم به وضعیت فعلی آن وابسته باشد. مثلاً وضعیت یک سیستم میتواند "ورود انجامشده" یا "ورود انجامنشده" باشد و عملکرد سیستم با توجه به این وضعیت متفاوت باشد.

- در TDD، ابتدا وضعیتهای مختلف را شناسایی کرده و برای هر یک، تست مناسب مینویسید.


مثال:

فرض کنید در سیستم خود وضعیت کاربر بهصورت "login" یا "logout" میباشد:


class User {
constructor() {
this.loggedIn = false;
}
login() {
this.loggedIn = true;
}
logout() {
this.loggedIn = false;
}
isAuthenticated() {
return this.loggedIn;
}
}
describe('State Testing', () => {
it('should be authenticated after login', () => {
const user = new User();
user.login();
expect(user.isAuthenticated()).toBe(true);
});
it('should not be authenticated after logout', () => {
const user = new User();
user.login();
user.logout();
expect(user.isAuthenticated()).toBe(false);
});
});


۱۰.۴ تستهای انتقال (Transition Testing)

- تستهای انتقال برای بررسی صحت عملکرد تغییرات بین وضعیتهای مختلف سیستم به کار میروند. این نوع تست برای اطمینان از صحت منطق انتقال از یک حالت به حالت دیگر استفاده میشود.

- در TDD ابتدا انتقالها را با تست پوشش میدهید و سپس منطق کد را برای اطمینان از صحیح بودن انتقالها پیادهسازی میکنید.


مثال:

فرض کنید یک سیستم دارید که وضعیت سفارش را مدیریت میکند:


class Order {
constructor() {
this.state = 'created';
}
confirm() {
if (this.state === 'created') {
this.state = 'confirmed';
}
}
ship() {
if (this.state === 'confirmed') {
this.state = 'shipped';
}
}
}
describe('Transition Testing', () => {
it('should transition from created to confirmed', () => {
const order = new Order();
order.confirm();
expect(order.state).toBe('confirmed');
});
it('should transition from confirmed to shipped', () => {
const order = new Order();
order.confirm();
order.ship();
expect(order.state).toBe('shipped');
});
});

```


۱۰.۵ ترکیب Spy و Mock

-تعریف Mock: برای شبیهسازی رفتار وابستگیهای خارجی یا توابع استفاده میشود، مثلاً درخواست به یک API خارجی یا دسترسی به دیتابیس. Mockها به ما این امکان را میدهند که در محیط تست کنترل بیشتری روی وابستگیها داشته باشیم.

-تعریف Spy: برای مشاهده رفتار یک تابع خاص از آن استفاده میکنیم، مثلاً برای بررسی اینکه آیا یک تابع فراخوانی شده و چند بار فراخوانی شده است.


مثال واقعی B2B با ترکیب Spy و Mock

فرض کنید شما یک سرویس سفارشگیری به نام `OrderService` دارید که باید اطلاعات سفارش را از طریق API خارجی ارسال کند. پس از موفقیت در ارسال سفارش، این سرویس وضعیت سفارش را در دیتابیس ذخیره میکند. میخواهیم این سناریو را تست کنیم و در این تست، درخواست به API خارجی را mock کنیم و تابع ذخیره در دیتابیس را با استفاده از spy مانیتور کنیم تا مطمئن شویم همه چیز بهدرستی انجام میشود.


// OrderService.js
const axios = require('axios');
const Database = require('./Database');
class OrderService {
async createOrder(orderDetails) {
try {
const response = await axios.post('https://api.example.com/orders', orderDetails);
if (response.data.status === 'confirmed') {
await Database.saveOrder({
...orderDetails,
orderId: response.data.orderId,
status: 'confirmed',
});
}
}
catch (error) {
throw new Error('Order creation failed');
}
}
}
module.exports = OrderService;


// OrderService.test.js
const OrderService = require('./OrderService');
const Database = require('./Database');
const axios = require('axios');
jest.mock('axios');
jest.mock('./Database');
describe('OrderService Tests with Spy and Mock', () => {
it('should call saveOrder after successful API response', async () => {
// Mocking the external API call
axios.post.mockResolvedValue({
data: {
orderId: '12345',
status: 'confirmed',
},
});
// Spying on the saveOrder method to ensure it is called correctly
const saveOrderSpy = jest.spyOn(Database, 'saveOrder').mockResolvedValue(true);
const orderService = new OrderService();
const orderDetails = { productId: 'P001', quantity: 10 };
await orderService.createOrder(orderDetails);
// Check that saveOrder was called
expect(saveOrderSpy).toHaveBeenCalledTimes(1);
expect(saveOrderSpy).toHaveBeenCalledWith({
productId: 'P001',
quantity: 10,
orderId: '12345',
status: 'confirmed',
});
});
it('should not call saveOrder if API request fails', async () => {
// Mocking the API to simulate a failure
axios.post.mockRejectedValue(new Error('API Error'));
const saveOrderSpy = jest.spyOn(Database, 'saveOrder');
const orderService = new OrderService();
const orderDetails = { productId: 'P001', quantity: 10 };
await expect(orderService.createOrder(orderDetails)).rejects.toThrow('Order creation failed');
// Ensure saveOrder was never called
expect(saveOrderSpy).not.toHaveBeenCalled();
});
});



توضیح:

- Mock کردن API خارجی با axios.post:

در تست اول، axios.post.mockResolvedValue برای شبیهسازی یک پاسخ موفق استفاده شده است. این به ما این امکان را میدهد که رفتار API خارجی را در محیط تست کنترل کنیم.

در تست دوم، از axios.post.mockRejectedValue برای شبیهسازی یک خطا استفاده شده است تا بررسی کنیم که تابع ما بهدرستی با خطاها برخورد میکند.


- Spy کردن saveOrder:

در هر دو تست، از jest.spyOn برای مشاهده رفتار تابع saveOrder استفاده شده است.

در تست اول، spy تضمین میکند که تابع saveOrder دقیقاً یک بار با پارامترهای صحیح فراخوانی شده است.

در تست دوم، spy تضمین میکند که در صورت شکست API، تابع saveOrder اصلاً فراخوانی نشده است.


مزایای استفاده از ترکیب Spy و Mock

1. کنترل وابستگیهای خارجی: با استفاده از mock برای شبیهسازی APIها و منابع خارجی، میتوانید رفتارهای مختلف را بدون نیاز به دسترسی به سرویسهای واقعی شبیهسازی کنید.

2. مانیتور کردن عملکرد توابع داخلی: با spy، میتوانید مطمئن شوید که توابع داخلی مانند ذخیره داده در دیتابیس بهدرستی و به تعداد موردنظر فراخوانی میشوند.

3. بهبود دقت تستها: ترکیب این دو ابزار باعث میشود تستهای شما دقیقتر و جامعتر باشند، زیرا هم ورودیهای خارجی را شبیهسازی میکنید و هم نحوه استفاده از این ورودیها در توابع داخلی را نظارت میکنید.

نتیجهگیری و جمعبندی

استفاده از TDD در پروژههای B2B با چالشهای متعددی همراه است که این چالشها ناشی از وابستگیهای پیچیده، حجم بالای دادهها، تغییرات مداوم نیازهای کسبوکار، و تعاملات پیچیده بین ماژولها میباشد. برای غلبه بر این چالشها، استفاده از تکنیکهای Mock و Stub، تستهای قراردادی، تستهای حالت و گذار، و تستهای پارامتریشده میتواند به توسعهدهندگان کمک کند که تستهایی جامعتر، پایدارتر، و با پوشش بهتر داشته باشند.


همچنین، ترکیب Continuous Integration (CI) با TDD میتواند هزینهها و زمان لازم برای توسعه را کاهش دهد و از کیفیت کد اطمینان حاصل کند. با استفاده از این رویکردها، توسعهدهندگان میتوانند سیستمهای B2B را با کیفیت بالاتر و قابلیت اعتماد بیشتر توسعه دهند و به نیازهای مشتریان به بهترین شکل پاسخ دهند.

tddb2bnodejsweb developmentspy mock
برنامه نویس علاقه مند به طراحی الگوریتم
شاید از این پست‌ها خوشتان بیاید