اگر پستهای قبلی این سری رو خونده باشید میدونید که من در این تجربهام میخواستم جوابی برای چندتا سوال در مورد نوشتن تستهای نرمافزار پیدا کنم. اگv بخوام در مورد سوالهایی که من در قسمت اول پرسیدم یک جمعبندی داشته باشم. به نظرم میشه نگرانیهای من رو در دو دسته خلاصه کرد:
در قسمت دوم تونستیم جواب نسبی به سوال اول بدیم: زبان Gherkin به همراه test coverage بر پایه لیست فیچرهای مورد انتظار در سیستم باید بتونه نگرانی اینکه چیزی از قلم افتاده باشه و تستی براش نوشته نشده باشه رو تا حد زیادی رفع کنه.
حالا میمونه دسته دوم نگرانی من و اینکه چطور تشخیص بدیم تستمون درست نوشته شده؟ همونطور که دیدیم ما ابزارهای خوبی در تستهای دیگه مثل تست واحد داریم که متاسفانه اینجا به کار ما نمیان. با این حال از پیدا کردن یک جواب برای سوال دوم هم خیلی دور نیستیم.
با توجه به هزینهبر بودن تستهای E2E (از لحاظ زمان و منابع مورد استفاده) قطعا نمیتونیم سراغ از کار انداختن سیستم به روشهای مختلف و اطمینان از قرمز شدن تستها بریم (منظورم ایده فضایی تست جهش در قیمت قبل هست). حالا که صحبت از تستهای سبز و قرمز شد، ما دیدیم که وقتی شروع به کار روی یک باگ یا فیچر میکنیم، سیستم هنوز باگ رو داره (و یا فیچر جدید رو نداره)، پس اگر تست رو قبل از رفع باگ بنویسیم، تست قبل از تغییر کد نرمافزار قرمز هست، و بعد از تغییر کد سبز میشه؟ ما میتونیم با همین نکته راه حلی برای سوال بعدی هم پیدا کنیم. کافیه کار رو از نوشتن تست شروع کنیم و اینطوری یک تست برای تغییرات مورد انتظارمون داریم که از سیستم مشکلدار (قبل از رفع مشکل) برای معنیدار بودنش استفاده میکنیم. من این روکجا دیدم؟ آهان، در مورد BDD گفتیم که خیلی خوبه چون حتی یک آدم غیر فنی هم میتونه با نوشتن تست به زبان Gherkin بهمون دقیقا بگه که انتظاراتش از سیستم چی هست که برآورده نشده و در این صورت، تستمون قبل از اینکه شروع به کار کنیم آماده هست!
به این روش که ما قبل از نوشتن کدهای برنامه، تستهامون رو بنویسیم، Test Driven Development یا TDD گفته میشه و BDD حالت توسعه یافتهای از TDD هست. در واقع تفاوتشون اینطوریه که اگه تست ما در سطح پایینتر و به شکل تست واحد باشه، اصطلاح کلیتر TDD رو براش استفاده میکنیم ولی اگه در سطح فیچر و رفتار کلی سیستم باشه، بهش میگیم BDD (تست BDD فقط محدود به Gherkin نیست و میتونه به شکلهای مختلف طراحی بشه). این نکته که تستهای BDD در مورد رفتار بیرونی سیستم صحبت میکنن و کاری به اجزای داخلی سیستم ندارن باعث میشه که BDD گزینه خوبی برای تعریف نیازها توسط تیمهای دیگه (حتی تیمهای فنی دیگه) باشه. البته این به معنی جدا بودن BDD و TDD از هم نیست و حتی در مشکل یا فیچری که توسط تست BDD به ما گزارش میشه، معمولا باید اون رو به چند کار کوچکتر بشکنیم و اینجاست که TDD و تست واحد دوباره وارد وارد مساله میشن.
خب تفاوت TDD و تست رگرسیون چیه؟ اگر یادتون باشه، تست رگرسیون تستی بود که بعد از هر تغییر در سورس برنامه اجرا میشد و تغییر رفتار سیستم و تغییر نتیجه تستها رو فوری شناسایی میکرد. خب اینا رو که TDD هم میتونه انجام بده؟ بله، در واقع اگر بخوایم روی تعاریف سختگیری کنیم، تفاوتهایی بینشون هست، ولی اگر خیلی رو اسمها تاکید نداشته باشیم، میشه گفت اگر تستی رو قبل از شروع کار مینویسید، از TDD پیروی میکنید، و اگر اجرای این تست رو بعد از هر تغییر در سورس نرمافزار تکرار میکنید، ازش به عنوان تست رگرسیون استفاده میکنید (مثل تست واحد که میتونه جزو هر دو دسته به حساب بیاد).
تست TDD و BDD فقط یک ابزار در متدلوژیهای توسعه نرمافزار (مثل اسکرام) نیستن و بخش مهمی از این فرآیند و ابزار تضمین کیفیت نرمافزارها هستن، چیزی که برای متدهای agile و انتشار مداوم نرمافزار خیلی مهمه. تجربه من هم در این مدت این بود که TDD انقدر خوب با متدلوژی اسکرام سازگاره که نه تنها چندین مشکل رو همزمان برامون حل میکنه، که برای بعضی مشکلها، تنها راه حل ممکنه!
متد TDD در سه مرحله توصیف میشه:
وقتی فیچر درخواستی یا باگ سیستم بهتون گزارش میشه و شما شروع به نوشتن تست میکنید. در این مرحله همه تستهایی که اضافه میکنید باید قرمز باشن (fail بشن). یکی دیگه از مزیتهای این مرحله (به جز چیزایی که بالاتر گفتم) اینه که شما مطمئنید اون چیزی که میخواید روش کار کنید یک scope کاملا مشخص داره و تمام چیزی که قراره بنویسید در تستها مشخص شدن (مثلا وقتی برنامهنویس بخشی از چیزی که ازش خواستن رو متوجه نشده یا اشتباه متوجه شده). به این خاطر هم، خیلی تیمها ترجیح میدن تستها رو یه برنامهنویس بنویسه و تسک رو یکی دیگه انجام بده.
در این مرحله شما دنبال مشکل میگردید و با ایجاد هر تغییر، تستها رو اجرا میکنید تا ببینید سبز میشن یا نه؟ این قسمت از کار، برای حل مشکل توسط برنامهنویسها (به خصوص کسی که تازه وارد تیم شده و با سورسهای نرمافزار خیلی آشنا نیست) ارزش زیادی میتونه داشته باشه، برای مثال وقتی برنامهنویس حدس میزنه که چجوری مشکل رو میشه برطرف کرد، اون رو انجام میده و با اجرای تستها امتحان میکنه که حدسش برای تمام حالات درست بوده یا نه و دیگه نیازی نیست موقع اضافه مردن هر خط به هزارتا چیز مختلف فکر کنه.
در نهایت بعد از سبز شدن تستهای مربوط به تسکی که روش کار میکردید، میتونید با اجرای همه تستها، مطمئن بشید که با این تغییر جای دیگهای از سیستم ایراد پیدا نکرده (یا این کار رو به تست رگرسیون و سیستم CI بسپرید).
حالا که اون نقص رو در نرمفزار برطرف کردید، این نرمافزار داره بدون ایراد کار میکنه و الان وقتشه که کدهایی که نوشته شدن تمیز بشن (تمیز کردن در کدنویسی، یا بهتر نوشتن منطق پیاده شده، اطمینان از اینکه کد شما convention های نرمافزار رو رعایت میکنه و .. ). با اجرای مداوم تستها هم میتونید مطمئن باشید که تستها هنوز سبز موندن و قسمتی از کدهاتون حین ریفکتور مشکل پیدا نکرده.
متد TDD چند تا مزیت دیگه هم داره، از جمله اینکه وقتی شما با این متد کار میکنید تعداد باگ کمتری در سیستم شما ممکنه بوجود بیاد (چون خیلی چیزها دارن به شکل مداوم و حین توسعه تست میشن). یک مزیت دیگه اینکه تعداد تستهای شما با تعداد فیچرها و باگفیکسهای سیستم در حال رشد هست (coverage همیشه رقم بالایی میمونه).
در آخر
بعد از این جستجوی روشهای مختلف و مطالعه در مورد تست نرمافزار، که برای خودم تجربه جالبی بود، در نهایت به این نتیجه رسیدم که اگر TDD در اسکرام جدیتر گرفته می شد، ما از روز اول و بدون اینکه خودمون بدونیم بیشتر راه رو رفته بودیم. و البته با توسعه TDD و استفاده از BDD میتونستیم ارزش خیلی بیشتری به تستهامون بدیم.
امیدوارم این رشته پستها به کارتون اومده باشه. سعی کردم اشارهای در داخل این متنها به خیلی از چیزایی که به نظرم در تست نرمافزار برام جال بودن داشته باشم. اگر فرصت بشه، سعی میکنم در آینده هم بیشتر در مورد تست نرمافزار بنویسم، چون به نظرم موضوع جالب و بسیار پرچالشی هست که در اکوسیستم ایرانی خیلی جای نوشتن و کار کردن داره.