اصول Test Driven Development

اصول Test Driven Development

تاریخ : چهار شنبه 24 آبان 1396

تست واحد نوع خاصی از آزمون با یک هدف بسیار مشخص و مجموعه ای از ویژگی ها است. خوشبختانه، تعریف پایه تست واحد نسبتا ساده است :

Unit Test یک تست است که یک نیاز مشخص برای یک متد مشخص را آزمایش می‌کند(قانون یک نیاز/یک متد one requirement/one method rule)

آزمون‌های واحد که قانون یک نیاز/یک متد را رعایت می‌کنند، ویژگی‌های زیر را هم به عنوان یک unit test‌ دارند:

  • هدفمند (Targeted) : آزمون‌های واحدی که یک چیز (شامل مجموعه‌ای از ورودی‌ها) را در یک زمان آزمایش می‌کنند، هدف‌گیری‌شده هستند.

  • جدا شده (Isolated) : کدی که در حال تست آن هستید باید از کد اصلی برنامه و وابستگی‌های خارجی یا رویدادها جدا بوده و ایزوله شده باشد.

  • قابل تکرار و قابل پیش بینی (Repeatable & Predictable) : یک آزمون واحد باید قابلیت بارها تکرار مجدد را داشته باشد (Repeatable باشد) و با فرض اینکه کد در حال تست و خود تست تغییر نکنند، هر دفعه همان نتیجه را تولید کند (قابل پیش‌بینی یا Predictable باشد)

  • مستقل (Independent) : این آزمون‌ها باید مستقل باشد، به صورت کلی هیچ تضمینی در خصوص ترتیب اجرا unit test ها وجود ندارد و بنابراین تست‌های نوشته شده توسط شما نباید انتظار یا نیاز به این مساله داشته باشند.

مفهوم Test DRIVEN Development

بیشتر برنامه‌نویس‌هایی که کار با آزمون‌های واحد را شروع می‌کنند، ابتدا کد برنامه‌شان را می‌نویسند و بعد unit test ها را. این گام مشترک و حتی می‌توان گفت اولین گام منطقی برای ورود به دنیای TDD و unit testing است. بالاخره نمی‌شود یک تست نوشت وقتی چیزی برای تست کردن وجود ندارد. خیلی از این برنامه‌نویس‌ها این کار را با یک روش و نیت خوب شروع می‌کنند: حتماً بعد از نوشتن کد، تست مربوط به کد را هم می‌نویسند. آن‌ها نوشتن تست‌ها را فراموش نمی‌کنند یا به خاطر یک کار مهم دیگر به تاخیر نمی‌اندازند. اما در واقعیت، تعهد به نوشتن تست کار بسیار دشواری است و تقریباً همه برنامه‌نویس‌ها بعد از مدتی دچار TED یا Test Eventually Develpoment می‌شوند و در واقع می‌گویند که بالاخره یک روزی تستش می‌کنیم و تا آن یک روزی ممکن است زمان زیادی طول بکشد یا حتی هرگز فرا نرسد!اولین D در TDD مخفف Driven هست.

ایده این روش این است که اولین کاری که برنامه‌نویس انجام می‌دهد نوشتن تست بر اساس ویژگی مورد انتظار فعلی نرم‌افزار (specification) است که روی آن کار می‌کند. این تست‌ها باید fail شوند چرا که قابلیتی که می‌خواهند آزمایش کنند هنوز به وجود نیامده است. در این شرایط کار برنامه‌نویس این خواهد بود که ساده‌ترین کد ممکن را بنویسد یا تست pass شود. اگر نرم‌افزار امکانات مورد انتظار (specification) بیشتری دارد، تست‌های بیشتری بنویسید و چرخه refactor و بهینه کردن کد را ادامه دهید. وقتی همه مشخصات نرم‌افزار تست داشتند و تست‌هایشان pass می‌شد نرم‌افزار شما آماده است. عرضه‌اش کنید!

اصول Test Driven Development

عمل TDD به نظر ساده است، اما این نشان دهنده یک تغییر اساسی در نحوه توسعه دهندگان برای دستیابی به توسعه نرم افزار است. در نتیجه، تمرین و نوشت تست کمی طول می کشد و چند بار استفاده از آن ممکن است کمی طبیعی باشد. اما اکثر توسعه دهندگان متوجه می شوند که در نهایت فقط نوشتن کد است، فقط شما ابتدا کد آزمون را می نویسید. سخت ترین بخش TDD حفظ نظم و انضباط و ادامه تمرین است.

چرا باید از TDD استفاده کنم؟

برخی از این مزایا واضح هستند و بعضی نه. شاید واضح‌ترین مزیت این باشد که کد شما وقتی کاملاً منطبق بر نیازهای مورد انتظار نرم‌افزار نیست،‌ مشکلات و باگ‌های کمتری خواهد داشت. یکی از انواع باگ‌هایی که TDD می‌تواند به صورت کامل حذفشان کند، "باگ‌های زامبی" هستند:

باگ‌هایی که به نظر می‌رسد رفع شده‌اند ولی چند build‌ بعدتر دوباره ظاهر می‌شوند!

وقتی رسیدگی به یک باگ یا مشکل به یک TDD کار محول می‌شود، اولین کاری که انجام می‌دهد نوشتن یک تست جدید است که باگ را آشکار و تست را fail می‌کند. بعد از این کار، برنامه‌نویس روش عادی کار در TDD را دنبال می‌کند:

آن‌قدر کد بنویس که تست مورد نظر pass شود و بقیه تست‌ها هم همچنان pass شده باقی بمانند.

مزیت دیگر استفاده از TDD بهبود کیفیت کد است. همان‌طور که گفته شد در TDD برنامه‌نویس‌ها باید ساده‌ترین کد برای pass شدن تست‌ها را بنویسند:

ساده‌ترین و کوتاه‌ترین کد که معمولاً کیفیت بیشتری دارد. همچنین این کدها خوانایی بیشتری دارند که باعث می‌شود نگهداری کد ساده‌تر شود.

مزیت دیگر استفاده از TDD حذف موثر کدهای مرده از برنامه شماست.

کدهای مرده یا Dead Code کدهایی هستند که در برنامه هستند اما هیچ وقت اجرا نمی‌شوند. این کد ممکن است یک متد یا کلاس باشند که هیچ وقت فراخوانی یا ارجاع داده نشدند یا بخشی از یک شرط باشند که هیچ وقت محقق نخواهد شد.

با استفاده از TDD شما فقط کدهایی را می‌نویسید که برای pass شدن تست نیاز دارید. اگر تست‌ها بر اساس نیازمندی‌های نرم‌افزار باشند، هیچ کدی از برنامه نیست که اجرا نشود و کدهایی که با روش TDD ایجاد می‌شوند همیشه مورد استفاده قرار می‌گیرند. با این حال تغییرات در نرم‌افزار به مرور زمان ممکن است باعث شوند یک متد که امروز مورد استفاده قرار می‌گیرد فردا هیچ استفاده‌ای نداشته باشد.

با مانیتور کردن کدها در TDD اگر کدی داشته باشید که در تستی مورد استفاده قرار نگرفته از دو حال خارج نیست:

یا یک تست از دست شما در رفته، یا آن کد یک کد مرده (dead code) است و باید حذف شود.

شناخت و فهمیدن چندریختی مبتنی بر اینترفیس (interface based Polymorphism) از مهارت‌های ضروری TDD‌ است. وقتی در این سری نوشته‌ها به مبحث mocking‌ و وابستگی‌ها (dependencies) برسیم، دانش شما در این حوزه هر روز به کار خواهد آمد.


منابع مورد مطالعه جهت جمع آوری این مطلب:
http://www.araye.net/Pages/30-Days-Of-TDD
https://www.telerik.com/blogs/30-days-tdd-day-one-what-is-tdd


نظرات