دربارهٔ این فصل
یادداشتِ همراه نوارتا حالا برایتان پیش آمده که بخواهید یک تغییر خیلی کوچک در برنامهتان بدهید، اما سیستم خودکار تستها با کوهی از خطاهای عجیبوغریب مانع کارتان شود؟ چطور میشود مطمئن بود کدی که امروز مینویسیم فردا با یک تغییر ساختار بیمورد خراب نمیشود و توسعهدهندگان را کلافه نمیکند؟
فصل دوازدهم از کتاب «مهندسی نرمافزار در گوگل» اثر «تیتوس وینترز، تام مانشریک و هایروم رایت» به ما نشان میدهد که چطور تستهای پایدار و کارآمد بنویسیم که به جای اتلاف وقت، به بهرهوری مهندسان کمک کنند.
اولین چالش بزرگ در تستنویسی، تستهای شکننده هستند؛ یعنی تستهایی که با یک تغییر ساختاری بیخطر در کد اصلی که هیچ باگی هم ایجاد نکرده، از کار میافتند. راهحل این است که روی تست واحد (Unit Test) تمرکز کنیم که به تست کردن کوچکترین واحد رفتار کد گفته میشود و باید از طریق رابطهای عمومی (Public APIs) به جای جزئیات پیادهسازی با سیستم تعامل کند. فرض کنید در حال توسعه یک پردازشگر تراکنش هستید؛ اگر به جای دستکاری متدهای داخلی، دقیقا مثل یک کاربر واقعی از درگاه عمومی کلاس استفاده کنید، تغییرات ساختاری داخلی کد، تست شما را نمیشکند و تنها زمانی خطا میگیرید که قرارداد واقعی سیستم نقض شده باشد.
دومین ایدهٔ کلیدی این است که به جای تعاملات، وضعیت (State) سیستم را تست کنید. خیلی از برنامهنویسان با کمک ابزارهای ساخت جایگزین تست (Test Double) که نمونههای شبیهسازیشده برای جایگزینی اشیای واقعی در تست هستند، بررسی میکنند که سیستم چه توابع یا متدهایی را فراخوانی کرده است. این تستهای تعاملی به شدت شکننده هستند چون به نحوه رسیدن سیستم به نتیجه کار دارند، در حالی که تست وضعیت فقط بررسی میکند که نتیجه نهایی و وضعیت سیستم بعد از اجرا چه بوده است.
سومین اصل، تمرکز روی رفتارها به جای متدها است. اگر برای هر متد یک تست بنویسید، با بزرگ شدن متدها، تستها هم پیچیده و گیجکننده میشوند. رویکرد درست این است که هر تست را به یک رفتار مشخص اختصاص دهید؛ رفتارهایی که با ساختار مشخصِ آمادهسازی، اقدام و بررسی نتیجه نوشته میشوند. نام تستها هم باید گویای همین رفتار باشد تا هر زمان تستی شکست خورد، از روی نامش دقیقاً بفهمیم چه چیزی خراب شده است بدون اینکه مجبور باشیم کل کدهای تست را زیر و رو کنیم.
چهارمین نکته مهم این است که از قرار دادن منطق برنامهنویسی مثل حلقهها و شرطها درون کدهای تست خودداری کنید. کدهای تست باید کاملاً خطی، ساده و قابل بازرسی با یک نگاه باشند؛ اگر تست شما خودش نیاز به تست کردن داشته باشد، مسیر را اشتباه رفتهاید. پیامهای خطا هم باید کاملاً صریح باشند و مقادیر مورد انتظار و مقدار واقعی دریافتی را به روشنی نشان دهند تا نیازی به جستجوی دستی در فایلهای تست نداشته باشید.
در نهایت، در اشتراکگذاری کد بین تستها به جای اصل DRY که به پرهیز از تکرار کد اشاره دارد، باید به سمت اصل DAMP برویم که بر عبارات توصیفی و معنادار تمرکز دارد. یعنی کمی تکرار در کدهای تست ایرادی ندارد اگر باعث شود هر تست کامل، مستقل و کاملاً خوانا بماند و خواننده مجبور نباشد برای فهمیدن منطق تست، بین فایلهای مختلف جابهجا شود.
به عنوان پیشنهاد عملی، به شما پیشنهاد میکنم در پروژهٔ بعدیتان قبل از نوشتن تست برای متدهای داخلی، ابتدا رفتار سیستم را از زاویه دید مصرفکننده تعریف کنید و مطمئن شوید کدهای تست شما آنقدر صریح هستند که حتی یک سال بعد هم بدون ابهام خوانده شوند.
امیدوارم این مرور کوتاه به شما کمک کند تستهای پایدارتر و مفیدتری بنویسید و از توسعه نرمافزار لذت ببرید.