چرا تست نمی‌نویسیم؟

از موارد استثنا که بگذریم، معمولا برنامه‌نویس‌های تازه‌نفس اشتیاق زیادی به نوشتن تست برای کدهایشان ندارند. خیلی از بحث‌ها در تیم‌های توسعه نیز ناظر به همین موضوع است: چرا باید تست بنویسیم؟ چقدر تست بنویسیم کافی است؟ چرا اینجا لازم نیست تست بنویسیم؟ و ...

مطالب مختلفی در مورد راه‌های اصولی نوشتن تست وجود دارد؛ لذا در این مقاله به جای پرداختن به آن‌ها، تلاش شده تا به دلایلی که باعث می‌شود کمتر سراغ تست برویم، اشاره شود و درک بهتری از شرایط حاصل گردد. همچنین در ادامه، راه‌هایی برای بهبود این شرایط پیشنهاد خواهد شد.

«تست کردن» یا «تست نوشتن»؟ مسئله این است

اگر اولین لحظه‌ای که توانستید دستوری را - در قالب متن - به کامپیوتر بفهمانید به خاطر بیاورید، احتمالا حس فوق‌العاده‌ی پس از آن را هم به خاطر خواهید آورد؛ شما نه به آن دلیل که توانسته‌اید متنی را تایپ کنید، بلکه به خاطر اینکه توانسته‌اید آن را تست کنید، هیجان‌زده شدید. نکته دقیقا اینجاست! تازه‌کارترین برنامه‌نویس‌ها هم هرگز فراموش نمی‌کنند «برنامه‌ای که به تازگی نوشته‌اند» را تست کنند، بلکه صرفا دلیلی نمی‌بینند آن را در قالب کد مکتوب کنند؛ زیرا:

  • مطمئن هستند کد کنونی درست کار می‌کند. مخصوصا مواقعی که قسمت کاملا جدیدی را توسعه داده‌اند؛ چراکه در فرایند توسعه، بارها و بارها عملکرد مورد نظرشان را به صورت دستی تست کرده‌اند.
  • مطمئن هستند آنچه قرار است به عنوان تست بنویسند، پاس می‌شود؛ لذا انگیزه‌ای برای صرف وقت و نوشتن «کدی که نتیجه‌اش مشخص است» ندارند.
  • ترجیح میدهند وقتی را که صرف نوشتن تست می‌کنند، به توسعه‌ی قابلیتی جدید و یا رفع یک مشکل دیگر اختصاص دهند.
  • ممکن است نوشتن تست فرایند پیچیده، زمانبر و یا مبهمی برای آن‌ها باشد.
  • ممکن است با خود بگویند اصلاً چه کسی می‌خواهد این تست را اجرا کند؟
  • ممکن است تست را وظیفه‌ی فرد یا تیم دیگری بدانند.

درباره هر یک از مسائل یاد شده می‌توان دلایل متقابل آورد (چنانچه آورده‌اند) و توضیح داد که چرا نوشتن تست با وجود این دلایل، غالبا سودمند است. اما به نظر می‌رسد مسئله بیشتر به تجربه مرتبط باشد تا دانش؛ توسعه‌دهنده‌ای که بر نوشتن تست اصرار می‌ورزد، احتمالا بیش از آنچه مزایای کوتاه‌مدت و بلندمدت این کار را «مطالعه» کرده باشد، «تجربه» کرده است. این همان دلیلی است که باعث می‌شود توسعه‌دهندگانِ باتجربه‌تر با اطمینان و لذت بیشتری تست بنویسند، تا برنامه‌نویسان تازه‌نفس.

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

جذابیت‌هایی که در ابتدا دیده نمی‌شوند

راستش را بخواهید، گاهی حق با برنامه‌نویس‌های تازه‌نفس است! اما مشکل اینجاست که آنها احتمالا دلایل اصلیِ بیهوده بودن تست را بیان نمی‌کنند. وقتی تست‌ها به صورت خودکار اجرا نمی‌شوند، عملا تبدیل به کدهای یکبارمصرف می‌شوند. یا وقتی فرایند بعد از تست (نظیر انتشار، استقرار، تست‌های non-functional و ...) همچنان به صورت دستی است و هیچ رویّه‌ی مشخصی ندارد، چه انگیزه‌ای برای مشخص کردن فرایند تست به تنهایی وجود دارد؟

در مقابل، وقتی همه یا بخشی از چرخه‌ی تولید نرم‌افزار خودکار است (به این معنا که فرایند آن مشخص و قابل اجرا توسط کامپیوتر است)، وجود تست‌هایی که آن‌ها هم به صورت خودکار بتوانند به اطمینان از درستی مراحل بعدی کمک کنند، جذاب‌تر خواهد بود.

از موارد بالا که بگذریم، نوشتن تست جذابیت‌هایی دارد که مشاهده‌ی آن‌ها نیازمند گذر زمان است. برخی از این موارد به شرح زیر است:

۱. وقتی بر روی یک قابلیت یا رفع ایراد تمرکز کرده‌اید، ممکن است از تأثیر آن بر زوایای دیگر محصول غافل باشید. اینجاست که تست‌هایی که قبلا برای آن زوایا نوشته‌اید، به دادتان میرسند.

تست‌ها، زمانی که شکست می‌خورند ارزش واقعی خود را نشان می‌دهند؛ نه زمانی که پاس می‌شوند!

۲. شرایطی را در نظر بگیرید که بتوان تنها با فشار یک دکمه، نسخه جدیدی از برنامه را منتشر کرد. در این شرایط وجود تست‌های ریز و درشت، مخصوصا زمانی که با کاربران متعدد یا سخت‌گیر مواجهید، این قوّت قلب را به شما می‌دهد که احتمالا آن دکمه قرار نیست روزگارتان را سیاه کند!

تست‌ها به همان اندازه که مانع خوشحالی تیم از ارائه سریع‌تر محصول می‌شوند، مانع ناراحتی آن‌ها از شکست محصول به دلیل سهل‌انگاری یا غفلت خواهند شد.

۳. «این مشکل را قبلاً دیده بودم»، «دوباره همان حالت پیش آمده»، «این را مگر حل نکرده بودیم؟!» و جمله‌های مشابه غالبا به این علت بیان می‌شوند که هنگام حل یک ایراد، برای آن تست نمی‌نویسیم و لذا بعداً به دلایل مختلف دوباره با آن مواجه می‌شویم.

نوشتن تست برای ایرادات، در واقع نوعی مستند کردن آن‌هاست؛ مستندی دقیق، پویا و قابل اجرا.

۴. علاوه بر موارد فوق، نوشتن تست خود باعث دقیق شدن مسئله و توجه به زوایای مختلفی می‌شود که لزوما هنگام پیاده‌سازی راه‌حل به ذهن توسعه‌دهنده نمی‌رسند. بارها رخ داده است که برخی تست‌هایی که بعد از توسعه‌ی یک قابلیت جدید نوشته می‌شوند - در کمال ناباوریِ توسعه‌دهنده - شکست می‌خورند!

جذابیت‌هایی که باید ایجاد شوند

مسئله‌ای که ممکن است ما را در مقابل نوشتن تست تنبل کند، عدم جذابیت این کار است. اگر از زاویه‌ی فلسفه‌ی لذت‌گرایی به موضوع نگاه کنیم، نوشتن تست، علاوه بر فواید بلندمدت، باید لذت‌های کوتاه‌مدت نیز به همراه داشته باشد؛ این نکته در نگاه اول ممکن است بی‌اهمیت جلوه کند؛ اما تجربه خلاف آن را نشان می‌دهد.

در ادامه به برخی نکات کوچک که به این جذابیت کمک می‌کنند اشاره می‌شود. این نکات در عین جذابیت کوتاه‌مدت، برکات بزرگ‌تری نیز به همراه خواهند داشت که توضیح آن در این مجال نمی‌گنجد.

ساده کردن فرایند تست

گاهی علت تنبلی در نوشتن تست، پیچیدگی و حجم کار غیرطبیعی در پیاده‌سازی آن است.

چارچوب‌های رایج تست معمولا بسیاری از پیچیدگی‌های عمومی آن را به عهده می‌گیرند (مواردی نظیر ایزوله بودن تست‌ها از هم، پاک کردن داده‌ها بعد از تست، نمایش نتایج، علت شکست‌ها و ...). اگر مواردی هستند که همچنان نویسنده‌ی تست را با حجم زیادی از کد تکراری یا پیچیده روبرو می‌کنند، این مسئله را به طور جداگانه حل کنید. یا از کسانی که به طور پیش‌فرض با نوشتن تست خوشحال هستند بخواهید بیشتر تست بنویسند!

If it hurts, do it more frequently!
Jez Humble

برای مطالعه‌ی بیشتر در این باره به اینجا مراجعه کنید.

محاسبه و نمایش میزان پوشش تست (Test Coverage)

تقریبا تمامی ابزارهای اجرای تست، میزان پوشش تست را نیز می‌توانند محاسبه کنند و یا دست‌کم گزارش‌هایی تولید می‌کنند که ابزارهای دیگری نظیر SonarQube یا CodeCov برای محاسبه با آن‌ها آشنا هستند.

اما مسئله مهم در اینجا محاسبه نیست، بلکه نمایش مقدار محاسبه شده است. سعی کنید میزان پوشش کد را در محل‌هایی که بیشتر در چشم هستند، نمایش دهید. یک نشان (badge) در مخزن پروژه، یک قسمت کوچک از مانیتور عمومی در تیم و ... می‌تواند جای مناسبی برای نمایش پوشش کد باشد.

چراغ تست!

اگر اهل زیاده‌روی هستید، وجود یک چراغ فیزیکی که نشانگر pass یا fail بودن تست‌ها در شاخه‌ی اصلی مخزن کد است، می‌تواند جذاب باشد. به این صورت که هنگام موفق بودن تست‌ها چراغ سبز، و به محض شکست آن‌ها قرمز شود.

  • وقتی چراغ قرمز است توقف کنید؛ همه‌ی کارهایتان را رها کنید و به رفع مشکلی که پیش آمده بپردازید. طبق توصیه‌ای که در کتاب Continuous Delivery بیان شده، اولویت اول تیم، پاس نگهداشتن پایپلاین است.
  • وقتی چراغ سبز است با خیالی آسوده به کارهای دیگر خود ادامه دهید.

نمایش پوشش تست به صورت عینی

یک قابلیت فوق‌العاده که اخیرا در گیت‌لب اضافه شده است، امکان مشخص کردن خطوط پوشش‌داده‌شده در MergeRequestهاست؛ به این صورت که در کنار خطوط کد، این که هر خط در تست‌ها پوشش داده شده یا خیر با رنگ سبز و زرد نمایش داده می‌شود. جزئیات بیشتر در این باره را می‌توانید در اینجا مشاهده کنید.

درگیر کردن تست در برنامه‌ریزی‌های زمانی

در جلسه‌ی برنامه‌ریزی اسپرینت (sprint planning) نشسته‌اید و مشغول تخمین زمان مورد نیاز برای انجام یک کار هستید. هرکس دلایل خود را برای زمانی که تخمین زده است، مطرح می‌کند. مدیر تیم به این اشاره می‌کند که از نظر او آن کار به علاوه‌ی زمان مورد نیاز برای تستش به فلان مقدار زمان نیاز دارد.

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

تعیین اهداف برای میزان پوشش تست‌ها

ما در سحاب یک پلکان بلوغِ توسعه‌ی محصول داریم که مراحل مختلف برای رسیدن به سطوح بالاتر کیفیت، در آن مشخص شده است. چندین آیتم از این پلکان، مربوط به انواع تست و ویژگی‌هایی مربوط به آنها (نظیر پوشش کد، پوشش نیازمندی و...) است.

همچنین برای اینکه وضعیت هر محصول در این پلکان مشخص و شفاف باشد، «نمو» را توسعه داده‌ایم. نمو ابزاری است که به تیم‌ها کمک می‌کند تا سطح کنونی محصول خود را مشاهده و برای ارتقای آن برنامه‌ریزی کنند.

این موضوع باعث شده مفهوم تست در شرکت ارزشمندی خود را بیش از پیش نشان دهد.

مخلص کلام!

خلاصه‌ی کلام اینکه سعی کنید تست‌ها دیده شوند! بهانه‌های درگیر شدن با نتیجه‌ی تست‌ها را زیاد کنید. آن‌ها هرچه بیشتر در کانون توجه قرار بگیرند، اشتیاق تیم برای تولیدشان بیشتر خواهد بود و در نهایت، وقتی تیم‌ها با فواید بلندمدت تست مواجه شوند، دیگر صورت مسئله‌ی «چرا تست نمی‌نویسیم؟» پاک شده است!