این مقاله یکی از مجموعه مقالات کتاب the clean coder هست. برای پیدا کردن دید بهتر از کل این مجموعه و محتوایی که تو این مقاله میخونید، پیشنهاد میکنم مقالهی صفرم رو هم یه نگاه بهش بندازید.
نیازمندیها
یکی از مواردی که خیلی مستعد سوء تفاهم بین تیم توسعه و مشتری (business) هست، «نیازمندیها» (requirements) است.
یه درس خیلی مهمی که عمو جان باب بعد از یهسری اتفاقات نسبتا دردناک یاد گرفته اینه که حتی خود مشتری هم از اول نمیدونه که دقیقا چیمیخواد! و همینطور که کار جلو میره، «کشف» میکنه که این چیزایی که میخواد دقیقا چی هستن ??♂️?. اون ایدهی اولیهای که از نیازهاشون و چیزی که میخوان دارن، اغلب اوقات تغییر میکنه.
دقت پیش از موعد
واقعیت اینه که تا قبل از اینکه محصول تا حدودی توسعه پیدا کنه (کدش زده شه و خروجی ملموسی ازش به دست بیاد)، نمیشه «دقیقا» فهمید که ماحصل نهایی قراره چی بشه (هرچند کلیات و حتی جزئیات تا حدود خوبی مشخص هستن). اما بعضی وقتا مشتریها و دولوپرها این موضوع رو متوجه نیستن. حالا مشکل کجاست؟ اینجاست که از طرفی مشتری میخواد قبل از اینکه اوکی نهایی رو واسه شروع پروژه بده، بدونه که دقیقا قراره چی تحویل بگیره و از اون طرف تیم توسعه هم قبل از اینکه بگه پروژه رو چه زمانی تحویل میده، میخواد بدونه که دقیقا قراره چی تحویل بده!
اصل عدم قطعیت
مشکل اینجاست که مشتری وقتی پیادهسازی واقعی اون نیازمندیهایی که به تیم توسعه داده رو میبینه تازه متوجه میشه که این اون چیزی نیست که واقعا میخواسته و تازه ایدههایی به ذهنش میرسه که چطور میتونه چیزی که تا الان ساخته شده رو بهتر کنه! البته نمیشه به مشتری هم ایراد گرفت چون بعد از دیدن پیادهسازی، اطلاعات بیشتری بهدست آورده (نسبت به زمان شروع پروژه) و همین باعث میشه که دید جدیدی به محصول پیدا کنه.
با این توضیحات فک کنم بخش «دقت پیش از موعد» رو هم بهتر متوجه میشیم.
اضطراب تخمین
دولوپرها فکر میکنن که حتما باید تخمین بزنن و فکر میکنن که این کار نیازمند «دقت»ه ?
مورد اول اینکه حتی اگه بینقصترین اطلاعات از چیزی که قراره پیادهسازی بشه رو به شما بدن (حداکثر دقت)، باز هم تخمینی که شما میزنید احتمال داره اختلاف زیادی با زمان واقعی پیادهسازی داشته باشه!
مورد دوم اینکه طبق چیزایی که تا الان متوجه شدیم، وسط راه «نیازمندیها» تغییر میکنن و تخمینهای اولیه میره رو هوا.
? دولوپرهای حرفهای میدونن که تخمین میتونه و اصلا باید براساس نیازمندیهایی باشه که دقت بالایی ندارن ( low precision requirements) و متوجه این موضوعه که این تخمینها صرفا تخمین هستن. (راجب تخمینهم تو فصلهای بعد توضیحات کاملتری هست).
ابهام دیرهنگام
هر ابهامی که توی سند نیازمندیها (قرارداد و غیره) هست، نیاز به یه جلسه بین ذینفعها داره. (ذینفعهایی مثل مشتری، تیم محصول، حتی خود تیم توسعه و …). بعضی وقتا ذینفعها «فرض میکنن» که کسی که این سند رو میخونه خودش متوجه میشه منظور ذینفع از نوشتن اون چی بوده (حتی وقتی مبهمه!)
یه مثالی میزنه عمو باب که بعضی وقتها این اختلاف ممکنه در این حد باشه که توی سند اومده باشه که مثلا این نرمافزار نیاز به فیچر بکاپگیری داره و منظور کسی که سند یا قرارداد رو نوشته، بکاپ گیری روزانهای باشه که یک سال هم نگهداری میشه ولی تیم محصول یا توسعه برداشتش این باشه که نیازی به نگهداری بکاپها نیست و هر وقت بکاپ جدیدی گرفته شد (یعنی یک روز بعد بهجای یک سال!)، بکاپ قبلی پاک میشه (شاید حتی با دلیل خوبی مثل صرفهجویی در فضای نگهداری (storage) و کاهش هزینه).
تست پذیرش
تعریفهای زیادی برای «تست پذیرش» وجود دارد:
تستهایی که کاربر قبل از تحویل نرمافزار انجام میده تا مشخص شه همهچی طبق سند پیادهسازی شده
تستهایی که تو بخش ارزیابی کیفیت (QA یا همون تیم تست) انجام میشه
و تعریفهای دیگه. تو این بخش، تعریف ما از تست پذیرش اینه:
«تستهایی که با همکاری ذینفعها و تیم توسعه نوشته میشه تا مشخص شه تعریف انجام شدن برای هر نیازمندی دقیقا چیه». احتمالا این تعریف الان مبهمه. پس ادامه بدیم ببینیم منظورش چیه.
تعریف انجام شدن
وقتی یه دولوپر میگه فلان تسک رو انجام دادم، منظورش دقیقا چیه؟ خبر بد اینکه هرکی یه تعریفی داره واسه خودش! ممکنه منظورش یکی از این موارد باشه:
فیچر کاملا پیادهسازی و تست شده و آمادهس برای دیپلوی
تسک رو زده و آمادهس که بفرسته واسه تست
کد رو زده و یکبار هم رانش کرده ولی حتی خودش هم خوب تستش نکرده و قاعدتا آمادهی فرستادنش به تیم تست هم نیست
آما دولوپرهای حرفهای یه تعریف واحد از انجام شدن دارن و وقتی میگن فلان تسک انجام شده، منظورشون اینه:
«کد بهطور کامل نوشته شده، تمام تستها پاس شدن، تیم تست هم تست کرده و اوکی داده و ذینفعها هم قبول کردن که این تسک الان انجام شده»
انجام همچین کاری بهنظر راحت نیست و حتی شاید انجام همهی این مراحل واسه هر تسک دست و پا گیر هم باشه.راه حل چیه؟ اینکه یهسری تست خودکار (automatic test) نوشته شه که فقط وقتی پاس بشن که همهی معیارهایی که تو تعریف بالا (تعریف دولوپرهای حرفهای از انجام شدن)هست ارضا شده باشه.
ارتباط
هدف تست پذیرش این سه تا چیزه؛ ارتباط، شفافیت (عدم وجود ابهام) و دقت.
وقتی همه (ذینفعها، تیم توسعه و تیم تست) روی تستهای پذیرش به توافق برسن، همه متوجه میشن که این سیستم قرار دقیقا چه ویژگیها و رفتاری داشته باشه.
رسیدن به همچین توافقی، وظیفهی همهس
دولوپرهای حرفهای این رو وظیفهی خودشون میدونن که با ذینفعها و تیم تست کار کنن تا این توافق بهدست بیاد و همه بدونن که این سیستم قراره دقیقا چی رفتاری داشته باشه.
خودکار کردن (automation)
تستهای پذیرش همیشه باید خودکار باشن. دلیلش هم سادهس: هزینه.
اگه شما هم مثل من فکر باشید، احتمالا دارید یه همچین فکرهایی میکنید که چهخبره بابا! بهنظر میرسه داره روغن داغ زیادی استفاده میشه! ادامه بدیم ببینیم دلیل اصرار به همچین کار نسبتا سختی چیه و مگه چقدر هزینه قرار داشته باشه انجام دستی تستهای پذیرش.
فعلا این عکس رو با هم ببینیم:
خب اگه به گوشهی سمت راست پایین عکس دقت کنیم دستهای یه بنده خدایی رو میبینیم که مدیر بخش ارزیابی کیفیت (QA) یه شرکت اینترنتی عریض و طویلِ و این چیزی که تو این صفحه میبینید صرفا فهرست سندی هست که بر اساس اون، ارزیابی کیفیت انجام میشه!!! بقول عمو باب این بزرگوار یه لشکر تستر تو کشورهای دیگه داره (احتمالا واسه کاهش هزینه) که هر ۶ هفته یکبار همهی این تستها رو انجام میدن و هربار حدود یک میلیون دلار هزینهی این تستها میشه. حالا جریان این عکس چیه؟ اینه که مدیر این آقا مدیر تو یه جلسه بهش گفته که قرار بودجهش نصف شه و بعد از جلسه در حالی که این تو دستش بوده از عمو باب سوال کرده که حالا کدوم نصف تستها رو باید بیخیال شیم! دیگه فکر کردن به ابعاد این فاجعه به عهدهی دانشجو.
در مقایسه با همچین هزینههایی، هزینهی تستهای خودکار خیلی خیلی ناچیزه. دولوپرهای حرفهای این رو وظیفهی خودشون میدونن که کاری کنن که تستهای پذیرش حتما بهطور خودکار نوشته شن.
ابزارهای زیادی مثل Selenium، FITNESSE و غیره وجود دارن که این امکان رو فراهم میکنن که افرادی که برنامهنویس نیستن هم بتونن این تستهای خودکار رو بخونن، بفهمن و حتی بنویسن.
کار اضافی
احتمالا همه تو ذهنمون الان این موضوع هست که واسه نوشتن تست خودکار واسه تستهای پذیرش باید کلی کار اضافه انجام داد. و خب حرف درستی هم هست. البته نه در مقایسه با چیزی که چند لحظه پیش عکسش رو دیدیم!
کاری که ما داریم انجام میدیم در واقع تعیین رفتار سیستم با سطح بالایی از دقته. این کار این مزیتها رو واسه ما داره:
تیم توسعه با دقت و شفافیت خیلی بالایی میدونه که دقیقا چی باید تحویل بده
ذینفعها کاملا مطمئن هستن که چیزی که قراره تحویل بگیرن، دقیقا اون نیازی که دارن رو فع میکنه
نوشتن تستهای خودکار (تا این دقت نباشه، نمیشه تستهای درست و مناسبی نوشت)
تستهای پذیرش رو کی مینویسه؟
تو یه دنیای ایدهآل، تیم تست و ذینفعها. ولی خب چون ذینفعها تو دنیای واقعی معمولا علاقه و وقت این کار رو ندارن، این کار میفتهی به عهدهی تحلیلگرهای کسب و کار (business analysts)، تیم ارزیابی تست و یا حتی تیم توسعه.
اگه قرار باشه تحلیلگرها و تیم تست این تستها رو بنویسن، معمولا اینجوریه که تیم تحلیل، تستهای روند سر راست (happy path) و تیم تست، تستهای حالتها و شرایط خاص (unhappy path) رو مینویسن.
اگه قرار باشه تیم توسعه این کار رو انجام بده، حتما باید به این نکته دقت کرد که دولوپر یا دولوپرهایی که این تستها رو مینویسن، همون دولوپرهایی نباشن که قراره نرمافزار رو توسعه بدن.
طبق قاعدهی «دقت دیرهنگام»، تستهای پذیرش باید تا جایی که ممکنه دیرتر نوشته شن. معمولا چند روز قبل از پیادهسازی هر فیچر. تو تیمهای چابک، معمولا بعد از اینکه یه فیچر برای اسپرینت بعد انتخاب میشه، این تستها نوشته میشن.
مذاکرات پسا تست
تستهایی که برای تست پذیرش نوشته میشن ممکنه دارای اشکالاتی باشن، مثلا خیلی پیچیده باشن، فرضیات بیربطی داشته باشن یا اینکه اصلا اشتباه باشن. و خب قاعدتا نوشتن کدی که همچین تستهایی رو قرار باشه پاس کنه، کار شدیدا آزاردهندهایه.
یه دولوپر حرفهای، با کسی که تست(ها) رو نوشته مذاکره میکنه راجب تست تا نهایتا تست بهتری نوشته شه.
یه کاری که به هیچ وجه نباید انجام بدیم اینه که وقتی دیدیم یه تست مشکل داره، بجای حرف زدن و صحبت کردن با کسی که اون تست رو نوشته، یجورایی لج کنیم و صرفا یه کدی بنویسیم که اون تست اشتباه رو پاس کنه!
تستهای پذیرش و تستهای واحد
نکتهی مهم اینه که بین تستهای واحد (unit test) و تستهای پذیرش (acceptance test) فرق هست.
تو تستهای واحد، هم نویسندهی تست یه دولوپره و هم مخاطب تست.
تو تستهای پذیرش، نویسنده و مخاطب، تیمهای کسب و کار (business) هستن (حتی وقتی که تیم توسعه نهایتا اونها رو مینویسه). این تستها مستندات نیازمندیها هستن و مشخص میکنن که سیستم باید چه رفتاری داشته باشه. (شاید بشه گفت از دیدگاه کاربر محصول، به محصول نگاه میشه تو این تستها)
درسته که تو هر دو نوع تست، یک چیز عملا داره تست میشه (رفتار سیستم) ولی با دو مکانیزم متفاوت این کار انجام میشه. تو تست واحد ما یجورایی از داخل دل و رودهی سیستم داریم تست میکنیم و متدها رو صدا میزنیم و آبجکت میسازیم از کلاسها و غیره. ولی تو تست پذیرش، از فاصلهی خیلی بیشتر و در سطح API یا حتی رابط کاربری (UI) داریم این کار رو انجام میدیم.
رابط کاربری گرافیکی
معمولا رابط کاربری گرافیکی (GUI) از اول آماده نیست و حتی اگر هم آماده باشه معمولا خیلی راجبش اظهار فضل میشه و دستخوش تغییراتی میشه. پس اگه ما تستها رو برای رابط کاربری بنویسیم، یا بعد از هر تغییر باید تستها رو بریزیم دور یا تغییر بدیم، یا اینکه کلا بیخیال تغییرات بشیم که خب هیچکدومش خوب نیست. پس راه حل چیه؟
راه حل اینه که تستها رو واسه لایهی API یی بنویسیم که دقیقا یه سطح پایینتر از رابط کاربریه. باید سعی کنیم تستهای خود رابط کاربری رو تو حداقل نگه داریم.
ادغام مداوم (Continuous Integration یا CI)
بعد از هر کامیت و قبل از ساختن (build) پروژه، سیستم CI باید تستهای واحد و پذیرش رو اجرا کنه.
اگه شما تجربههای متفاوتی از چیزی که عمو باب داره دارید خوشحال میشم با من و بقیهی کسایی که این مقاله رو میخونن در میون بذارید. تو تیمهایی با سایزها و پروژههای مختلف بهنظر شما این موضوع ثابته؟ همیشه نوشتن تست پذیرش منطقی و عملیه؟