ذینفعان خواستار رفع سریع یک باگ در برنامه هستند. چنین وصله ای پرهزینه است و همیشه راه حل کاملی ارائه نمی دهد. چرخه را با تست واحد (Unit Testing)، سرمایه گذاری ارزشمندی در کیفیت پروژه بشکنید.
واقعاً در مورد تست واحد چه احساسی دارید؟ آیا منطقی است که تمرکز را از توسعه دور کنیم تا تلاشهای تیم را برای بهبود پایگاه کد اختصاص دهیم؟ توسعهدهندگان همیشه از چرخههای خستهکننده ارزیابی کد، دادههای فیک، تعریف گروه(های) تست و امضا(های عملکرد آنها)، نوشتن تستها و سپس بازگشت به تغییر کد موجود لذت نمیبرند.
علاوه بر این، مدیران باید منابع تیم را در مقابل هزینه های اجرای تست واحد بسنجند. مهمترین دلایل ذکر شده برای انصراف از تست واحد شامل محدوده محدود پروژه، چه به دلیل یک تیم کوچک، یا ساعات کاری محدود در زمان انجام یک مهلت تعیین شده، و نگرانی های بودجه است.
اما افزودن تستهای واحد میتواند به اندازه بررسی تازه بودن شیر قبل از ریختن آن در قهوه، یکپارچه و کاربردی باشد. در دراز مدت، تست واحد به طور غیرقابل انکاری تجربه توسعه پروژه را افزایش می دهد و هزینه ها را کاهش می دهد، همانطور که در بررسی مزایای تست واحد و استراتژی های غالب برای اجرای آن خواهیم دید.
چگونه تست واحد برای پروژه های شما مفید است
تست واحد، فرآیند اجرای تستهای متمرکز بر روی تکههای کوچک (واحد) کد است تا بررسی شود که آیا کد در طول توسعه یک برنامه مطابق خواستهها عمل میکند یا خیر. شناسایی پیشگیرانه باگ ها از طریق تست واحد، زمان رفع باگ را کاهش می دهد و باعث صرفه جویی در هزینه می شود.
کاهش هزینه های پس از تولید
با گنجاندن دقیق تست واحد در فرآیند توسعه، کیفیت برنامه خود را افزایش می دهید. این برنامه با باگ ها و نقص های کمتری ارائه می شود تا مهندسان QA مستند کنند یا توسعه دهندگان آن را اصلاح کنند.
حجم کار سبکتر پس از تولید منجر به کاهش هزینههای پروژه و تکمیل سریعتر پروژه میشود. مدیران میتوانند دورههای قرارداد تیم QA محدود شده را پیشبینی و بودجهبندی کنند، پروژههای آتی یا وابسته را برای شروع زودتر برنامهریزی کنند، و توسعهدهندگان میتوانند حتی زودتر کار بر روی برنامههای جدید را شروع کنند.
همه اینها منجر به کاهش حتی بیشتر هزینه ها می شود. این اطمینانبخش است که بدانید کار اساسی را برای جلوگیری از بلایای احتمالی انجام دادهاید.
افزایش پتانسیل درآمد
یک تجربه برنامه کاربردی با کیفیت بالا و بدون باگ منجر به رضایت بیشتر کاربران نهایی وفادار می شود. اگر کاربران با گذاشتن نظرات آنلاین مثبت، یا با اشتراک گذاری با دوستان و خانواده، برنامه را توصیه کنند، پتانسیل درآمد برنامه به طور تصاعدی افزایش می یابد.
و بیایید از این واقعیت غافل نشویم که کمبود نقدهای منفی نیز می تواند منجر به موفقیت مالی شود. همانطور که مصرف کنندگانی وجود دارند که اطلاعات مطلوب را جستجو می کنند، کسانی نیز هستند که به طور خاص به دنبال بررسی های انتقادی به عنوان راهی برای جلوگیری از یک محصول یا خدمات معیوب هستند. می توان استدلال کرد که از منظر تجاری، کسب نظرات مثبت سودمند است، اما اجتناب از بررسی های مخرب بسیار مهم است.
توقفگاه مستندات
بررسی تست های واحد می تواند به سرعت بخشیدن به توسعه دهندگان جدید در یک برنامه کمک کند. هنگامی که یک پروژه تقسیم بندی می شود یا به تیم های جدا شده تقسیم می شود، بررسی تست های واحد تا حد زیادی به سمت پر کردن شکاف های دانش و ارائه بینش در مورد برنامه به عنوان یک کل می رود.
صرف نظر از سازماندهی و سبک ارتباطی یک تیم، تستهای واحد خواندن به توسعهدهندگان این امکان را میدهد تا اطلاعاتی را جمعآوری کنند که در غیر این صورت ممکن است به اندازه کافی مستند نشده باشند یا در زیر انباری از یادداشتها مدفون باشند.
اهداف هوشمند طبیعی
طبق ماهیت خود، تست واحد پروژه را به بخش های منظم و قابل هضم تقسیم می کند. این تقسیمبندیهای کد شخصیسازیشده، بهطور مؤثر به اهداف SMART (خاص، قابل اندازهگیری، قابل دستیابی، واقعبینانه و به موقع) تعریف شده تبدیل میشوند.
رسیدن به اهداف SMART با شفافتر کردن پیشرفت تیم برای رهبری و ذینفعان، به پروژه کمک میکند. توسعه دهندگان مجبور به برنامه ریزی از قبل و کدنویسی به شیوه ای سازماندهی شده هستند. به هر واحد کد یک تابع واحد اختصاص داده میشود و برای اطمینان از اینکه واحد مطابق مورد نظر عمل میکند، تست میشود. کد دارای تفکیک هایی از نگرانی ها است:
1. هر واحد پروژه از یک هدف واحد پشتیبانی می کند.
2. هر تابع در یک واحد فقط محدوده خود را انجام می دهد.
وجود واحدهای بایت اندازه ردیابی پروژه را تسهیل می کند. در مقایسه با تیمی که نقاط عطف آن نامشخص یا دور است، تیمی که به طور معمول واحد بعد از واحد را بررسی می کند، احتمالاً از این دو خوشحال تر است.
مقیاس پذیری بهبود یافته
تست واحد به خوبی برنامه ریزی شده بر مقیاس پذیری کد در تعدادی از جنبه ها تأثیر می گذارد و ما را به توانایی های زیر مجهز می کند:
معماری:
افزودن یا تعویض ویژگی ها در یک زمان معقول.
نتیجه مستقیم اجرای موثر تفکیک نگرانی ها
مهندسی:
مهندسانی را اضافه کنید تا راه حل ها را سریعتر ارائه دهند.
اثر مستقیم کد هدفمند، سازماندهی شده و خواناتر
تیمی:
تیم ها را به بخش های کوچکتر تقسیم کنید تا راه حل ها را سریعتر ارائه دهید.
پیامد مستقیم کد مدولار
بار:
استفاده بسیار بالاتر از حد انتظار را مدیریت کنید.
تنگناها را راحت تر شناسایی کنید.
اثرات غیر مستقیم کد جدا شده
کد قابل تست تمیز، ماژولار و قابل استفاده مجدد در محیط های دیگر است.
کد و ویژگی های پایدار
فرض کنید که قبلاً یک ویژگی گلوبال جستجو بر اساس نام را تست و اجرا کردهایم، و اکنون میخواهیم به کاربر نهایی امکان فیلتر کردن نتایج جستجو را بدهیم.
برای اضافه کردن این ویژگی، ما یک واحد جدید برای تابع فیلتر خود ایجاد می کنیم. ما میتوانیم مطمئن باشیم که واحدی که ویژگی جستجوی جهانی بر اساس نام را کنترل میکند، در صورت تست مجدد همچنان باید قبول شود. کد واحد جدید نباید کد را در واحدهای دیگر "break" کند.
رفع باگ پیشرفته
شناسایی و تصحیح علت اصلی یک باگ در کد واحد تست شده با سهولت بیشتری انجام می شود. بگویید ویژگی جستجو به درستی کار نمی کند. به جای یک سوزن کلاسیک در سناریوی انبار کاه، بررسی کل پایگاه کد برای علت اصلی، می توانید نتایج آزمون واحد را در ماژول جستجوی پروژه دوباره مشاهده کنید.
Refactoring کارآمد
تست واحد یک ویژگی به ما کمک می کند تا تأیید کنیم که طبق انتظار عمل می کند - حتی اگر منطق کد را اصلاح کنیم یا کتابخانه های شخص ثالث را به روز کنیم.
با استفاده از نمونه جستجوی گلوبال قبلی خود بر اساس نام، فرض کنید این ویژگی کاملاً کار می کند، اما به سرعت ملاس است. برای رفع مشکل سرعت، یک اصلاح بالقوه را پیاده سازی می کنیم (به عنوان مثال، الگوریتم را جایگزین می کنیم) و دوباره تست می کنیم. یک بار دیگر، می توانیم مطمئن باشیم که ویژگی خود را که قبلاً به خوبی تست شده است، «broken» نکرده ایم. این ویژگی همچنان باید تستهای واحد خود را پس از رفرکتور پشت سر بگذارد.
استراتژی های تست واحد
وقتی نوبت به تست می رسد، هیچ استاندارد جهانی وجود ندارد. در واقع، بحث های زیادی بین متخصصان وجود دارد که چقدر تست واحد برای موفقیت یک پروژه ضروری است. بین زمان سرمایه گذاری شده و کیفیت کد، معاوضه هایی وجود دارد. پس از تعیین محدوده تست واحد، مدیر پروژه باید از بین چندین استراتژی تست واحد انتخاب کند.
محدوده تست واحد
تست واحد را به عنوان یک بیمه نامه برای محافظت از پروژه خود در نظر بگیرید. اغلب، تحمل ریسک فرد است که تعیین میکند چقدر بیمه بخرد یا یک طرح تست واحد را چقدر گسترده اجرا کند. در یک انتهای طیف، کسانی هستند که برای به دست آوردن بیشتر هزینه بیشتری می کنند، هدف آنها پوشش حداکثری برای جلوگیری یا جلوگیری از فاجعه است. در انتهای دیگر کسانی هستند که از شانس خود استفاده می کنند. شاید آنها از نظر مالی یا در غیر این صورت انعطاف پذیر باشند و در موقعیت خوبی برای بازگشت از ضرر، در صورت بروز چنین اتفاقی، قرار بگیرند. اکثریت قریب به اتفاق مردم در جایی بین این دو طرز فکر قرار می گیرند.
محدوده طرح تست واحد معمولاً در یکی از سه الگوی زیر قرار می گیرد:
کل پایگاه کد به صورت متوالی
کل پایگاه کد به ترتیب اهمیت
فقط بخشهای حیاتی، شاید بخشهایی که بیشترین هزینه را برای هزینه تستی ما ارائه میدهند
الگوی سوم که به آن تست واحد هدفمند می گویند، با توجه به محدودیت های پروژه، اغلب کاربردی ترین است. در این مورد، ما کد را برای تست انتخاب میکنیم و روی قسمتهایی تمرکز میکنیم که برای موفقیت یک پروژه بسیار مهم هستند.
توسعه دهندگان نرم افزار مخصوصاً واجد شرایط هستند که دانش خود را از هدف هر قطعه کد به تست های برازش ترجمه کنند. زمانی که محدوده یک طرح مشخص شد، می خواهیم استراتژی را در نظر بگیریم و اتخاذ کنیم که برای پروژه ما کارآمد باشد.
رویکردهای تست واحد
استانداردهای صنعت عبارتند از:
تست پس از پیاده سازی، که در آن توسعه دهندگان تست هایی را پس از پیاده سازی ویژگی ها می نویسند.
توسعه تست محور (TDD)، که در آن توسعه دهندگان کد و تست را با هم برای هر مورد مورد نیاز ویژگی می نویسند.
ایده تست پس از اجرا میتواند برای مدیرانی که تمایل دارند توسعه را در رقابت برای ارائه محصولات به ذینفعان اولویتبندی کنند، جذاب باشد. بنابراین، تست پس از اجرا، روشی بسیار رایجتر از TDD است، که در مقایسه، به آرامی شروع میشود و نیاز به نظم و انضباط و صبر در طول مدت پروژه دارد.
هر دو رویکرد از مراحل اولیه یکسانی استفاده می کنند و فقط ترتیب عملیات آنها متفاوت است. در زیر این مراحل را نشان میدهد:
پس از اجرا :
مرحله 1. الزامات ویژگی را به موارد استفاده تبدیل کنید.
مرحله 2. کد را پیاده سازی کنید.
مرحله 3. موارد تست را تعریف کنید.
مرحله 4. تست ها را بنویسید، اجرا کنید و اعتبار سنجی کنید.
مرحله 5. کد را در صورت لزوم تصحیح کنید.
مرحله 6. پس از موفقیت آمیز بودن همه تستات، ویژگی را تأیید کنید.
TDD :
مرحله 1. الزامات ویژگی را به موارد استفاده تبدیل کنید.
مرحله 2. موارد تست را تعریف کنید.
مرحله 3. تست ها را بنویسید، اجرا کنید و اعتبار سنجی کنید.
مرحله 4. کد را پیاده سازی کنید.
مرحله 5. تست ها را دوباره اجرا کنید.
مرحله 6. کد را در صورت لزوم تصحیح کنید.
مرحله 7. پس از موفقیت آمیز بودن همه تستات، ویژگی را تأیید کنید.
این بحث بدون ذکر تست واحد ترکیبی کامل نمیشود، که در آن ویژگیهای پس از پیادهسازی را تست میکنیم و باگهایی را که در طول توسعه با TDD با آن مواجه میشویم برطرف میکنیم و برای هر باگ جدید تستهایی اضافه میکنیم.
نمونه های فنی
ما رویکردهای مختلف تست واحد را دیدهایم، اما آماده کردن پروژه ما برای تست واحد تمیز و متمایز در عمل به چه معناست؟ برای شروع یک اجرای تستی نمونه، ابتدا باید جدایی از نگرانی ها را فراهم کنیم که در آن هر واحد از یک هدف واحد پشتیبانی می کند و هر تابع یک وظیفه واحد را انجام می دهد.
فقط رابط یک واحد باید تست شود: حالتها و ویژگیهای داخلی که قرار است توسط واحدهای دیگر خوانده و/یا نوشته شوند باید حذف شوند. بنابراین، اگر یک واحد مسئول یک تابع ضرب باشد، میتوانیم تستی بنویسیم که اطمینان حاصل کند که ضرب به درستی انجام شده است (به عنوان مثال، 5x7=35)، اما ما بررسی نمیکنیم که ضرب در واقع چگونه اتفاق میافتد (به عنوان مثال، 5x7 در مقابل 7x5 در مقابل 7+7+7+7+7 و غیره).
بیایید تصور کنیم برنامه ای داریم که در آن می خواهیم سه فایل متنی را ارائه کنیم که عناوین آنها باید با رنگ فونت آبی نمایش داده شوند. ما با نوشتن کل برنامه برای ویژگیهایمان، بارگیری فایلها، و نشان دادن سرفصلها، به کارگیری بهترین شیوهها و جداسازی نگرانیهایی که قبلاً بحث شد، شروع میکنیم. سپس کد خود را در صورت لزوم تست و به روز می کنیم.
اکنون که کد ما پیاده سازی شده است، موارد تست را برای بررسی کامل ویژگی تعریف می کنیم:
آیا فایل ها لود می شوند؟
آیا متن فایل نمایش داده می شود؟
آیا رنگ فونت سرفصل های ما آبی است؟
در مرحله بعد، ما تست های واحد جداگانه برای واحدهای کد مربوطه می نویسیم:
تابعی که فایل ها را بارگذاری می کند
تابعی که متن را نمایش می دهد
تابعی که با قالب بندی سروکار دارد
ما میتوانیم این سناریوها را به زبان خوانا گرکین بررسی کنیم، که برای ارائه چنین رفتارهایی ساختار یافته است:
Feature: Load and display text from the files and display all headings in blue font color
Scenario: User loads file successfully
Given user navigates to the platform
And user navigates to the Import File page
When user selects the file and chooses Import
Then file is imported successfully
Scenario: File is loaded and text displays successfully
Given user navigates to the platform
And user navigates to the Import File page
When user selects the file and chooses Import
Then file is imported
And file text displays in its entirety
Scenario: File is loaded and text displays successfully and all headings display in blue font color
Given user navigates to the platform
And user navigates to the Import File page
When user selects the file and chooses Import
Then file text displays in its entirety
And all headings display in blue font color
هر سناریو باید تست واحد شود.
نسخه دمو تست واحد پس از اجرا
در رویکرد تست پس از اجرا، به شرح زیر عمل می کنیم:
1. کد را برای هر سه سناریو پیاده سازی کنید.
2. تست های این سناریوها را بنویسید.
3. تست ها را اجرا کنید.
اگر هر سه سناریو تست واحد را پشت سر بگذارند، کد ما خوب است. با این حال، اگر هر تستی با شکست مواجه شد، باید کد خود را تغییر دهیم و دوباره تست کنیم تا همه تستها با موفقیت انجام شوند.
ما می توانیم انتظار داشته باشیم که برخی از تست ها ممکن است شکست بخورند، زیرا آنها همزمان با ویژگی های مربوطه خود توسعه داده نشده اند. اگر تست مشکلی را نشان داد (به عنوان مثال، اگر سرفصل ها با فونت آبی نمایش داده نمی شوند)، باید کد را تغییر داده و دوباره تست کنیم.
نسخه ی دمو TDD
در TDD، قبل از نوشتن هر کدی، ما نیازمندی های پروژه را ویژگی به ویژگی به تست تبدیل می کنیم. از آنجایی که ما هنوز نرم افزار را پیاده سازی نکرده ایم، تست های ما در اولین اجرا با شکست مواجه می شوند. اما ما به هر حال این کار را انجام می دهیم تا یکپارچگی ساختار خود را تأیید کنیم: اگر نحو کد صحیح باشد، تست اجرا می شود و با شکست مواجه می شود. اما اگر ایراد داشته باشد، تست اجرا نمی شود و با خطای سینتکس مواجه می شویم.
حالا کد را پیاده سازی می کنیم و تست ها را دوباره اجرا می کنیم. برای هر شکستی که مواجه میشویم، کد خود را بهروزرسانی میکنیم و مجدداً تست میکنیم و تنها پس از تست موفقیتآمیز این ویژگی را تأیید میکنیم.
در ادامه مثال قبلی، اجازه دهید TDD را نشان دهیم. ما تست هایی را برای ویژگی خود تعریف و اجرا می کنیم (عنوان هایی که با رنگ فونت آبی نمایش داده می شوند). با فرض اینکه هیچ مشکل نحوی در تست ما شناسایی نشده است، اکنون آماده ایم کد خود را پیاده سازی کنیم و تست را دوباره اجرا کنیم:
تست های سناریوی اول را بنویسید.
کد را برای سناریوی اول اجرا کنید، تا زمانی که همه تستها قبول شوند، تکرار کنید.
مراحل 1 و 2 را برای هر سناریو باقی مانده تکرار کنید.
پس از اتمام این فرآیند، رویکرد TDD ما تکمیل می شود.
نتیجه
ما نشان دادهایم که تست واحد پروژه شما بیش از هزینههای خود در صرفهجویی مالی، پیشگیری از باگ ها و آرامشی که برای شما ایجاد میکند، هزینه خواهد داشت.
جمله هشداردهنده بنجامین فرانکلین - "یک اونس پیشگیری ارزش یک پوند درمان را دارد" - امروز هم صادق است. مانند یک بیمه نامه، تست واحد یک سرمایه گذاری ارزشمند است. ما برای اطمینان از اینکه می دانیم از بلایای احتمالی جلوگیری کرده ایم یا از آن جلوگیری کرده ایم، تست می کنیم، و این قیمتی ندارد.