چطور تست بنویسیم ؟

چندی پیش بر روی پروژه ای کار میکردم که برای اون نیاز بود تست بنویسم تا از درست کار کردن بخش های مختلف اون اطمینان حاصل کنیم. چیز هایی که نوشتم حاصل از تحقیق ها و تجربه شخصی ام از تست نوشتنه.فرض بر اینه که شما با برنامه نویسی و مفاهیم کلاس و متد و مشخصه های public و private آشنایی دارید.یکی از مهم ترین کار ها در توسعه نرم افزار تست نوشتنه. وقتی ما نرم افزاری رو تولید میکنیم باید مطمن باشیم که درست کار میکنه. برای اینکار میتونیم خودمون تک تک بخش های مختلف اون رو ببینیم و تست کنیم. یا اینکه یه مکانیزم اتوماتیک برای اینکار داشته باشیم. چون معمولا نرم افزار ها در طول زمان بزرگ میشن اینکه خودمون تک تک بخش های نرم افزار رو دستی تست کنیم غیر ممکن میشه. اگر برنامه نویسی نتونه برای کدش تست بنویسه اصلا نباید اسم برنامه نویس رو روش گذاشتاین جملع قطعا درسته! نوشتن تست یکی از مهارت های اصلی توسعه دهندگان حرفه ایه.نوشتن تست مزایای زیادی داره. شما وقتی تست مینویسید باید دقیقا ورودی و خروجی سیستمتون رو بدونید. همچنین باید انواع ورودی های احتمالی به سیستم رو هم در نظر بگیرید. همین باعث میشه که خودتون به درک بهتری از سیستم برسید و بعضا ممکنه تجدید نظری در سیستم و منطق و حتی بیزینستون کنین!همچنین تست نوشتن میتونه تا حدی کمکتون کنه تا یک معماری خوب برای برنامتون داشته باشید.نوشتن تست کمک زیادی به کسانی میکنه که میخواهند کد شما رو بخونن و با سیستم شما آشنا بشن. با یک نگاه به تست ها میشه فهمید که سیستم شما چه ورودی هایی رو قبول میکنه و چه چیزی رو به ما تحویل میده یا چه کاری رو برای ما انجام میده. به همین راحتی میشه با کلیت سیستم آشنا شد. انواع تست ها:تست در واقع یه برنامست که ما بهش میگیم ببین بخش های مختلف برنامم درست کار میکنه یا نه.به طور کلی تست هایی که ما برای نرم افزار مینویسیم به دو دسته Unit Test و Integration Test تقسیم میشن.Unit Test:این نوع تست ها وظیفه تست کردن بخش های مختلف برنامه را به صورت جداگانه برعهده دارد. مثلا فرض کنید میخواهیم ماشینی رو تست کنیم. Unit Test یعنی ما اجزای اون رو به تنهایی تست کنیم. مثلا ببینیم چرخش درست کار میکنه؟ فرمونش درست کار میکنه...وقتی داریم Unite Test انجام میدیم باید تاثیر قسمت های مختلف رو از بین ببریم. در مثال ماشین اگر بخواییم چرخ رو تست کنیم باید حواسمون باشه که فقط چرخ رو تست کنیم و مثلا عملکرد ترمز تاثیری نداشته باشه.Integration Test:وقتی دیدیم اجزای مختلف برنامه به تنهایی درست کار میکنند حالا نوبت به این میرسه که این اجزا رو در کنار هم تست کنیم. در مثال ماشین باید چرخ رو در حضور ترمز و فرمون تست کنیم. به این دلیل این نوع تست ها مهم اند چون ممکنه اجزای مختلف به تنهایی درست کار کنند اما در حضور همدیگه عملکرد مطلوبی نداشته باشن. مثلا فرض کنیم میخواییم رد شدن یک کامیون از داخل تونل رو تست کنیم. کامیون و تونل به تنهایی درست کار میکنند و وظیفشون رو انجام میدن. ولی کامیون نمیتونه از توی تونل رد شه!معمولا Unit Test ها چون فقط با یک جز از برنامه کار دارند سریعتر اجرا میشوند. یک کد استاندارد باید حدود 80 درصد آن Unit Test شده باشد. به عبارت دیگر Unit Test Coverage باید 80 درصد باشد. در مقابل Integration Test ها چون با اجزای مختلف برنامه در ارتباط هستند کمی کند تر اجرا میشوند. هرم تستهرم تستهرم تست دقیقا به ما نشون میده که تعداد Unit Test ها معمولا بیشتر از Integration Test هاست. و هر چه به سمت بالای هرم برویم زمان اجرای تست ها بیشتر میشود.TDDیکی از روش های پرطرفدار برای توسعه نرم افزار توسعه تست محور هست. به این شکل که شما ابتدا تست هایی برای برنامه مینویسید. بعد باید کد برنامه رو تکمیل کنید که این تست ها پاس بشن. برای این روش باید تمامی حالت هایی که ورودی برنامه شما قبول میکنه رو لیست کنین و براش تست بنویسین. این روش شاید در ابتدای کار کمی خسته کننده به نظر برسه و کار رو کند کنه. ولی قطعا در بلند مدت این روش میتونه کمک بزرگی بکنه به اینکه نرم افزار ما خالی از اشتباه و باگ باشه و اینکه سرعت توسعه رو به شدت تسریع میکنه.چند نکته:وقتی میخواهید از متد ‌TDD استفاده کنید باید دقیقا ورودی و خروجی برنامتون معلوم باشه.باید نوع نگاهتون رو تغییر بدید. مثلا فرض کنید که یک کلاس داریم وظیفش انتگرال گرفتنه. یک متد داره که ما ورودی به اون میدیم و انتگرال رو حساب میکنه. ما ابتدا تابع محاسبه انتگرال رو مینویسیم ولی خالی. بعد تست ها رو مینویسیم. مثلا انتگرال این عدد میشه این مقدار. بعد تابع محاسبه رو پیاده سازی میکنیم. ممکنه داخل این تابع از توابع private دیگه ای هم استفاده بشه. ولی مهم نیست. عملکرد این متد برای ما مهمه. ممکنه بگید که شاید توابع private ای که داخل تابع اصلی استفاده شدن مشکل داشته باشن. جواب اینه که ما باید تمامی تست کیس های مختلف رو لیست کنیم و بنویسیم. اگر یکی از توابع درونی مشکلی داشته باشه قطعا یکی از تست کیس ها به مشکل میخوره. اگر تمامی تست کیس ها رو نوشتیم و همه شون مشکلی نداشتن اطمینان حاصل میکنیم که این کلاس داره وظیفش رو به درستی انجام میده.کلاس ها و توابع private رو نباید نیاز باشه تست کنیم. اگر نیاز شد که یک کلاس یا تابع private رو تست کنیم باید بدونیم که معماری نرم افزار ما احتمالا مشکلی داره و باید بیشتر بهش فکر کنیم. (اگر با روش TDD توسعه بدید که اصلا نیازی به تست متد و کلاس های private نمیشه.)وقتی Unit Test مینویسیم باید حواسمون باشه که فقط یک چیز رو تست کنیم. مثلا هنگام تست نباید با دیتابیس یا نتورک ارتباط داشته باشیم. بلکه باید این ها رو شبیه سازی یا mock کنیم.فراموش نکنیم که برای نوشتن تست خوب و البته نرم افزار خوب باید به اصول SOLID مسلط باشیم.