چرا تستهای سبز همیشه به معنای سلامت نرمافزار نیستند؟
کشف کنید چرا پوشش کد ۱۰۰ درصدی و تستهای واحد سبز همیشه تضمینکننده کیفیت نیستند. بررسی یک مورد واقعی از خطای منطقی در سیستمهای مالی و امنیتی.
تصور کنید یک سیستم امنیتی بسیار پیشرفته برای ورودی ساختمان طراحی کردهاید. این سیستم حسگرهای دقیق، تشخیص چهره و لایههای حفاظتی متعددی دارد. شما بارها و بارها آن را در کارگاه آزمایش کردهاید و هر بار سیستم به درستی عمل کرده است. اما وقتی نوبت به بهرهبرداری میرسد، متوجه میشوید که در اصلی ساختمان اصلاً به این سیستم متصل نشده است. سیستم امنیتی شما در تنهایی خودش عالی کار میکند، اما عملاً هیچ تاثیری بر امنیت ساختمان ندارد چون در مدار اصلی قرار نگرفته است.
این دقیقاً اتفاقی است که در دنیای توسعه نرمافزار به دفعات رخ میدهد. ما تستهای واحد (Unit Tests) مینویسیم، پوشش کد (Code Coverage) را به صد در صد میرسانیم و با دیدن چراغهای سبز در ابزارهای CI/CD احساس امنیت میکنیم. اما حقیقت این است که یک مجموعه تست سبز، فقط ثابت میکند که کد در شرایط ایزوله درست کار میکند، نه اینکه لزوماً در دنیای واقعی هم کاربردی دارد.
داستان گیت امنیتی که هرگز بسته نشد
در یکی از پروژههای اخیر، تیمی روی یک «گیت انتشار» کار میکرد. وظیفه این قطعه کد این بود که قبل از نهایی شدن هر تراکنش مالی، مجموعهای از قوانین سفتوسخت را بررسی کند و در صورت وجود کوچکترین مغایرت، عملیات را متوقف سازد. توسعهدهندگان با وسواس عجیبی تمام حالتهای ممکن را تست کرده بودند. از ورودیهای خالی گرفته تا اعداد نجومی و کاراکترهای غیرمجاز؛ همه چیز با موفقیت پاس میشد.
گزارشهای تست نشان میداد که این بخش از سیستم پوشش ۱۰۰ درصدی دارد. در جلسات بازبینی کد، همه به کیفیت بالای این ماژول افتخار میکردند. اما یک ماه بعد، در بررسی گزارشهای مالی متوجه شدند که چندین تراکنش با خطاهای فاحش پردازش شدهاند. چطور چنین چیزی ممکن بود؟ وقتی کد را کالبدشکافی کردند، حقیقت تلخی آشکار شد: این گیت امنیتی هرگز در خط لوله اصلی (Pipeline) فراخوانی نشده بود.
کد تست شده بود، منطقش درست بود و در محیط آزمایشگاهی مثل ساعت کار میکرد، اما در کد اصلی، هیچ پیوندی میان جریان داده و این گیت وجود نداشت. تستها سبز بودند چون خودِ تستها کد را صدا میزدند، اما سیستم واقعی اصلاً از وجود چنین نگهبانی خبر نداشت.
چرا تستهای واحد به تنهایی کافی نیستند؟
تستهای واحد به ما میگویند که منطق یک تابع درست است. آنها به ما اطمینان میدهند که اگر عدد ۲ را به علاوه ۲ کنیم، خروجی حتماً ۴ خواهد بود. اما این تستها نمیتوانند بگویند که آیا ما اصلاً از این تابع در جای درست استفاده کردهایم یا خیر. در واقع، تستهای واحد نوعی «صحت داخلی» را تضمین میکنند، در حالی که ما به «صحت ساختاری» هم نیاز داریم.
برای درک بهتر، یک مثال بومی بزنیم. فرض کنید در حال ساخت یک اپلیکیشن پرداخت برای تاکسیهای اینترنتی در ایران هستید. شما کدی مینویسید که مبلغ کرایه را بر اساس مسافت و ترافیک محاسبه میکند. تستهای شما نشان میدهند که اگر مسافت ۱۰ کیلومتر باشد، قیمت درست محاسبه میشود. اما اگر در کد اصلی، دکمه «پرداخت» به جای فراخوانی این تابع، مستقیماً یک عدد ثابت را به بانک بفرستد، تمام آن تستهای درخشان بیفایده خواهند بود.
تله پوشش کد و توهم کیفیت
بسیاری از مدیران فنی و تیمها، درصد پوشش کد را به عنوان شاخص اصلی کیفیت در نظر میگیرند. این یک خطای استراتژیک است. پوشش کد فقط به شما میگوید کدام خطوط توسط تستها لمس شدهاند، اما نمیگوید که آیا آن خطوط در سناریوی واقعی کاربر هم اجرا میشوند یا نه.
وقتی تمرکز بیش از حد روی سبز ماندن تستها باشد، تیمها ناخودآگاه به سمت نوشتن تستهایی میروند که پاس کردنشان راحتتر است. در این حالت، ما با پدیدهای مواجه میشویم که من آن را «تستهای نمایشی» مینامم. تستهایی که فقط هستند تا آمار را بالا ببرند، اما هیچ ریسک واقعی را پوشش نمیدهند.
چگونه از این بنبست خارج شویم؟
برای اینکه در تله تستهای سبزِ بیخاصیت نیفتیم، باید چند تغییر اساسی در رویکردمان ایجاد کنیم:
۱. تستهای یکپارچگی (Integration Tests): به جای تمرکز صرف بر واحدها، باید جریان داده را از ابتدا تا انتها تست کنیم. آیا وقتی کاربر روی دکمه کلیک میکند، داده واقعاً از آن گیت امنیتی عبور میکند؟
۲. تست در محیط شبهواقعی: استفاده از محیطهای Staging که دقیقاً مشابه محیط تولید هستند، کمک میکند تا ناهماهنگیهای اتصالات (Wiring) زودتر مشخص شوند.
۳. بازبینی کد با نگاه سیستمی: در جلسات Code Review، به جای اینکه فقط بپرسید «آیا این تابع درست کار میکند؟»، بپرسید «این تابع کجای این پازل بزرگ قرار میگیرد و چه کسی آن را صدا میزند؟».
۴. استفاده از تستهای نفوذ و سناریوهای منفی: سعی کنید سیستم را در محیط واقعی خراب کنید. اگر گیت امنیتی شما واقعاً متصل باشد، باید بتواند یک نفوذ عمدی را در محیط تست شناسایی و مسدود کند.
واقعیت پشت پرده کدهای تمیز
نوشتن کد تمیز و تستهای پاس شده لذتبخش است، اما هدف نهایی ما تولید نرمافزاری است که در دست کاربر درست عمل کند. تستها ابزاری برای رسیدن به اطمینان هستند، نه خودِ هدف. اگر روزی دیدید که تمام تستهایتان سبز است اما هنوز در محیط تولید با باگهای منطقی ابتدایی روبرو میشوید، وقت آن است که نگاهی به سیمکشی سیستم بیندازید. شاید نگهبان شما پشت درِ بستهای ایستاده است که اصلاً محل عبور و مرور نیست.
منبع: dev.to