Unit Test چیست؟ چگونه از خطاهای برنامهنویسی جلوگیری میکند؟
تصور کنید در حال توسعه یک فروشگاه اینترنتی هستید. کاربران میتوانند محصولات مختلفی را به سبد خرید اضافه کنند، کد تخفیف وارد کنند و در نهایت هزینه سفارش خود را پرداخت کنند.
همهچیز بهدرستی کار میکند تا اینکه تصمیم میگیرید فقط یک بخش کوچک از محاسبات قیمت را تغییر دهید. بعد از این تغییر، متوجه میشوید که کد تخفیف دیگر درست محاسبه نمیشود یا حتی قیمت نهایی سفارش اشتباه نمایش داده میشود!
حالا باید دوباره تمام بخشهای برنامه را بررسی کنید تا بفهمید کدام قسمت دچار مشکل شده است.
اما اگر قبل از ایجاد این تغییرات، راهی وجود داشت که بتوانید بهصورت خودکار عملکرد هر بخش از کد را بررسی کنید، چه میشد؟
اینجاست که مفهومی به نام Unit Test یا تست واحد اهمیت پیدا میکند.
تست واحد یکی از مهمترین روشهای آزمایش نرمافزار است که به برنامهنویسان کمک میکند قبل از انتشار برنامه، از درست کار کردن بخشهای مختلف کد خود مطمئن شوند.
اما Unit Test دقیقاً چیست و چرا برنامهنویسان حرفهای از آن استفاده میکنند؟
Unit Test چیست؟
Unit Test یا تست واحد، روشی برای بررسی عملکرد کوچکترین بخشهای مستقل یک برنامه است.
به زبان ساده، برنامهنویس بهجای اینکه منتظر بماند تا کل نرمافزار آماده شود و سپس آن را آزمایش کند، هر بخش از کد را بهصورت جداگانه بررسی میکند تا مطمئن شود مطابق انتظار کار میکند.
این بخش کوچک میتواند مواردی مانند موارد زیر باشد:
- یک تابع برای محاسبه قیمت نهایی محصول
- یک متد برای محاسبه مالیات
- یک تابع برای اعتبارسنجی شماره موبایل
- یک متد برای محاسبه امتیاز کاربران
- یک تابع برای بررسی معتبر بودن کد تخفیف
برای مثال، فرض کنید متدی نوشتهاید که قیمت نهایی یک محصول را با توجه به درصد تخفیف محاسبه میکند.
میتوانید چند ورودی مختلف به آن بدهید و بررسی کنید که آیا خروجی مورد انتظار را تولید میکند یا خیر.
اگر قیمت محصول ۱۰۰ هزار تومان و میزان تخفیف ۲۰ درصد باشد، خروجی باید ۸۰ هزار تومان شود.
در Unit Test این نتیجه بهصورت خودکار بررسی میشود و اگر خروجی با مقدار مورد انتظار تفاوت داشته باشد، تست با شکست مواجه خواهد شد.
یک مثال ساده از Unit Test
فرض کنید در یک پروژه فروشگاهی با زبان C#، متدی برای محاسبه قیمت پس از تخفیف نوشتهایم:
public decimal CalculateDiscount(
decimal price, decimal discount)
{
return price - (price * discount / 100);
}
این متد دو ورودی دریافت میکند:
- price: قیمت اولیه محصول
- discount: درصد تخفیف
حالا میخواهیم مطمئن شویم که این متد همیشه نتیجه درستی تولید میکند.
برای این کار میتوانیم از یک تست واحد با استفاده از فریمورک xUnit استفاده کنیم:
[Fact]
public void CalculateDiscount_ShouldReturnCorrectPrice()
{
var result = CalculateDiscount(100000, 20);
Assert.Equal(80000, result);
}
در این مثال:
- [Fact] مشخص میکند که این متد یک تست است.
- CalculateDiscount(100000, 20) متد اصلی را با ورودی مشخص اجرا میکند.
- Assert.Equal بررسی میکند که آیا نتیجه با مقدار مورد انتظار برابر است یا خیر.
اگر نتیجه ۸۰ هزار تومان باشد، تست موفق میشود؛ اما اگر مثلاً ۹۰ هزار تومان برگردد، تست شکست میخورد و برنامهنویس متوجه وجود مشکل خواهد شد.
البته این فقط یک نمونه ساده برای آشنایی با مفهوم Unit Test است و در یک پروژه واقعی، متد مورد آزمایش و تست آن معمولاً در کلاسها و پروژههای جداگانه قرار میگیرند.
Unit Test چگونه کار میکند؟
فرایند اجرای تست واحد معمولاً از سه مرحله اصلی تشکیل میشود که به الگوی Arrange، Act، Assert معروف هستند.
linkedin.com
۱. Arrange (آمادهسازی)
در این مرحله، اطلاعات و شرایط موردنیاز برای اجرای تست آماده میشوند.
مثلاً مشخص میکنیم قیمت محصول ۱۰۰ هزار تومان و درصد تخفیف ۲۰ درصد است.
۲. Act (اجرا)
در این مرحله، متدی را که قصد آزمایش آن را داریم اجرا میکنیم.
مثلاً متد محاسبه تخفیف را با اطلاعات مرحله قبل فراخوانی میکنیم.
۳. Assert (بررسی نتیجه)
در مرحله آخر، نتیجه واقعی را با نتیجه مورد انتظار مقایسه میکنیم.
اگر هر دو برابر باشند، تست موفق است و در غیر این صورت، تست شکست میخورد.
این سه مرحله کمک میکنند تستها ساختار مشخصی داشته باشند و خواندن و نگهداری آنها سادهتر شود.
تفاوت Unit Test با تست دستی چیست؟
فرض کنید در یک اپلیکیشن خرید آنلاین، بخشی برای محاسبه هزینه ارسال سفارش نوشتهاید.
در روش تست دستی (Manual Testing)، خودتان برنامه را اجرا میکنید، یک سفارش ثبت میکنید و بررسی میکنید که هزینه ارسال درست محاسبه شده است یا خیر.
اما اگر از Unit Test استفاده کنید، میتوانید سناریوهای مختلف را از قبل تعریف کنید تا کامپیوتر آنها را بهصورت خودکار اجرا و نتیجه را بررسی کند.
برای مثال:
- سفارش کمتر از ۵۰۰ هزار تومان: هزینه ارسال دریافت شود.
- سفارش بیشتر از ۵۰۰ هزار تومان: ارسال رایگان باشد.
- سفارش با مبلغ صفر: نتیجه مطابق قوانین برنامه بررسی شود.
تفاوت اصلی این دو روش در نحوه انجام آزمایش است.
تست دستی به دخالت انسان نیاز دارد، اما تست واحد میتواند بارها و بارها بهصورت خودکار اجرا شود.
البته Unit Test جایگزین کامل تست دستی نیست؛ هر کدام کاربرد متفاوتی دارند و در پروژههای حرفهای معمولاً در کنار یکدیگر استفاده میشوند.
چرا Unit Test برای برنامهنویسان اهمیت دارد؟
شاید در پروژههای کوچک بتوانید با چند بار اجرای برنامه، از درست کار کردن کدها مطمئن شوید؛ اما وقتی پروژه بزرگتر میشود، بررسی تمام بخشها بهصورت دستی بسیار زمانبر و دشوار خواهد بود.
تصور کنید یک نرمافزار حسابداری دارید که صدها متد برای محاسبه مالیات، حقوق کارکنان، موجودی حسابها و گزارشهای مالی در آن وجود دارد.
تغییر یک متد ممکن است بهصورت ناخواسته عملکرد بخش دیگری را تحت تأثیر قرار دهد.
Unit Test به شما کمک میکند قبل از اینکه چنین مشکلاتی به دست کاربران برسند، آنها را شناسایی کنید.
مهمترین دلایل استفاده از تست واحد عبارتاند از:
۱. شناسایی سریع خطاها
با اجرای تستها میتوانید متوجه شوید کدام بخش از کد مطابق انتظار کار نمیکند.
۲. جلوگیری از بازگشت خطاها (Regression)
گاهی برنامهنویس برای اضافه کردن یک قابلیت جدید، بخشی از کدهای قبلی را تغییر میدهد و ناخواسته باعث خراب شدن عملکرد قبلی میشود.
تستهای واحد کمک میکنند بعد از هر تغییر، عملکرد بخشهای قبلی نیز بررسی شود.
۳. افزایش اطمینان هنگام تغییر کدها
وقتی برای بخشهای مختلف برنامه تست نوشته باشید، با خیال راحتتری میتوانید کدها را اصلاح کنید یا قابلیتهای جدیدی به پروژه اضافه کنید.
۴. کاهش هزینه نگهداری نرمافزار
پیدا کردن خطا در مراحل اولیه توسعه معمولاً سادهتر و کمهزینهتر از زمانی است که نرمافزار منتشر شده و کاربران با مشکل مواجه شدهاند.
۵. کمک به طراحی بهتر کد
نوشتن تست واحد باعث میشود برنامهنویس به وظایف هر کلاس و متد دقیقتر فکر کند و کدهایی با مسئولیت مشخصتر و وابستگی کمتر بنویسد.
آیا Unit Test تمام خطاهای برنامه را پیدا میکند؟
یکی از تصورات اشتباه درباره Unit Test این است که اگر تمام تستهای یک پروژه موفق باشند، دیگر هیچ خطایی در نرمافزار وجود نخواهد داشت!
در واقع چنین چیزی درست نیست.
تست واحد فقط بخشهایی را بررسی میکند که برای آنها تست نوشته شده است و موفقیت آنها تضمین نمیکند که تمام قسمتهای نرمافزار بدون مشکل کار میکنند.
برای مثال، ممکن است متد محاسبه قیمت تخفیف بهدرستی کار کند، اما هنگام اتصال به درگاه پرداخت، برنامه با خطا مواجه شود.
چون Unit Test معمولاً بخشهای کوچک برنامه را بهصورت مستقل بررسی میکند، نمیتواند بهتنهایی تمام مشکلات مربوط به ارتباط بین بخشها، پایگاه داده یا رابط کاربری را شناسایی کند.
به همین دلیل در توسعه نرمافزار از روشهای آزمایش دیگری نیز استفاده میشود.
تفاوت Unit Test با Integration Test چیست؟
دو مفهوم مهم در دنیای تست نرمافزار، Unit Test و Integration Test هستند.
هر دو برای شناسایی خطا استفاده میشوند، اما هدف متفاوتی دارند.
promleeblog.com
Unit Test (تست واحد):
در این روش، یک بخش کوچک از برنامه بهصورت مستقل آزمایش میشود.
مثلاً بررسی میکنیم که آیا متد محاسبه تخفیف، با دریافت قیمت و درصد تخفیف، نتیجه درستی تولید میکند یا خیر.
Integration Test (تست یکپارچگی):
در این روش، ارتباط بین چند بخش مختلف نرمافزار بررسی میشود.
مثلاً در یک فروشگاه اینترنتی بررسی میکنیم که آیا ثبت سفارش، ذخیره اطلاعات در پایگاه داده و بهروزرسانی موجودی محصول بهدرستی با یکدیگر کار میکنند یا خیر.
بنابراین تفاوت اصلی این دو در محدوده آزمایش است؛ Unit Test روی یک بخش کوچک تمرکز دارد، اما Integration Test تعامل بین چند بخش را بررسی میکند.
برای نوشتن Unit Test از چه ابزارهایی استفاده میشود؟
بسته به زبان برنامهنویسی و محیط توسعه، ابزارهای مختلفی برای نوشتن و اجرای تست واحد وجود دارند.
برخی از پرکاربردترین آنها عبارتاند از:
- xUnit: یکی از فریمورکهای محبوب برای نوشتن تست واحد در C# و پروژههای .NET.
- NUnit: فریمورکی قدرتمند برای آزمایش نرمافزارهای نوشتهشده با زبان C#.
- MSTest: ابزار تست مایکروسافت برای پروژههای .NET.
- Jest: یکی از ابزارهای پرکاربرد برای تست پروژههای JavaScript و TypeScript.
- pytest: فریمورکی محبوب برای نوشتن تست در زبان Python.
- JUnit: یکی از ابزارهای شناختهشده برای تست واحد در زبان Java.
این ابزارها امکاناتی مانند اجرای خودکار تستها، بررسی نتایج و گزارش خطاها را در اختیار برنامهنویسان قرار میدهند.
چه زمانی باید Unit Test بنویسیم؟
بهترین زمان برای نوشتن تست واحد، همزمان با فرایند توسعه نرمافزار است؛ نه اینکه تمام پروژه را بنویسیم و در پایان به فکر آزمایش آن بیفتیم.
برای مثال، زمانی که یک متد جدید برای محاسبه قیمت، اعتبارسنجی اطلاعات یا مدیریت سفارشها مینویسید، میتوانید تست مربوط به آن را نیز ایجاد کنید.
همچنین بعد از اصلاح یک خطا، بهتر است تستی بنویسید که مطمئن شوید آن مشکل دوباره تکرار نمیشود.
نکته مهم این است که لازم نیست برای تکتک خطوط کد یک تست جداگانه بنویسید. بهتر است بخشهایی را در اولویت قرار دهید که منطق مهمتری دارند یا اشتباه در آنها میتواند مشکلات جدی ایجاد کند؛ مانند محاسبات مالی، پرداختها، ثبت سفارش و مدیریت اطلاعات کاربران.
جمعبندی
Unit Test یا تست واحد یکی از مهمترین مهارتها در توسعه نرمافزار است که به برنامهنویسان کمک میکند بخشهای کوچک برنامه را بهصورت مستقل و خودکار آزمایش کنند.
با استفاده از این روش، میتوان خطاها را زودتر شناسایی کرد، از خراب شدن قابلیتهای قبلی جلوگیری کرد و با اطمینان بیشتری کدها را توسعه داد.
البته تست واحد بهتنهایی تضمین نمیکند که یک نرمافزار کاملاً بدون خطا باشد؛ بلکه یکی از ابزارهای مهم در کنار تست یکپارچگی، تست دستی و سایر روشهای آزمایش نرمافزار محسوب میشود.
اگر قصد دارید به یک برنامهنویس حرفهای تبدیل شوید، بهتر است در کنار یادگیری زبانهایی مانند C#، Python و JavaScript، با مفاهیمی مانند Unit Test نیز آشنا شوید؛ زیرا برنامهنویسی حرفهای فقط نوشتن کد نیست، بلکه اطمینان از درست کار کردن آن هم اهمیت زیادی دارد.
فرایانه؛ جایی که مفاهیم پیچیده برنامهنویسی را به زبان ساده، کاربردی و قابل فهم آموزش میدهیم.