مقدمه ای بر TDD
در سال ۱۹۹۹ گروهی از توسعه دهندگان متدولوژی eXtreme Programing یا به اختصار XP را پدید آوردند. در این متدولوژی به جای آنکه ابتدا کد برنامه را نوشته و سپس آن را تست نمایند، برعکس عمل میکردند. یعنی ابتدا کد تست را مینوشتند، سپس کدهای برنامه را برای پاس کردن آن تستها اضافه میکردند. به این شیوه، برنامه نویسی اول تست[۱] میگفتند که به توسعه تست محور[۲] یا به اختصار TDD نیز معروف است. TDD حاصل تکامل فرآیند تولید نرم افزار است و بسیاری از مشکلات و چالشهای تولید نرم افزار را رفع میکند. از این رو است که امروزه تبدیل به مهمترین سبک برنامه نویسی شده است. در ادامه اصول، مزایا و نحوه برنامه نویسی TDD بیان میگردند.
[۱] Test-First Development
[2] Test Driven Development
TDD به عنوان یک سبک برنامه نویسی به طرز فریبندهای ساده است. بر عکس روش سنتی برنامه نویسی که شما مینشستید و یک فرم یا کلاس ایجاد میکردید، در TDD شما با نوشتن یک تست، کار را شروع میکنید. با نوشتن یک تست در واقع یک نیازمندی را تعریف میکنید. در حالی که شما مشغول تعریف تستها هستید، به یک نسخه اجرایی از نیازمندیها میرسید. زمانی که این تستها پاس شوند، نیازمندیها نیز برآورده شده اند.
وقتی که شما اولین تست را مینویسید، تست شما پاس (Pass) نمیشود. اولین دلیل برای رد شدن تست (Fail) این است که برنامه کامپایل نمیشود، زیرا کد تست کننده تلاش میکند تا از کلاسی که هنوز تعریف نشده، نمونه گیری کند. فرض کنید میخواهیم برنامهای بنویسیم که نتیجه یک عدد به توان عدد دیگر را محاسبه کند. کد تست این برنامه بدین صورت است[۱]:
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
|
[TestFixture] public class PowerClassTest { [Test] public void PowerMethodTest() { var powerClass = new PowerClass(); var result = powerClass.Power(2, 10); Assert.AreEqual(result, 1024); } } |
در کد فوق PowerMethodTest() یک متد تست است. در این متد یک نمونه از کلاس PowerClass ایجاد شده و متد Power() با پایه ۲ و توان ۱۰ فراخوانی میشود. سپس نتیجه این متد با مقدار مورد انتظار (۱۰۲۴) مقایسه میشود. بنابراین این تست بررسی میکند که آیا کلاس PowerClass به درستی عمل تقسیم را انجام میدهد یا خیر.
مشخص است که این تست رد خواهد شد، چون اصلاً کلاس PowerClass و متد Power() تعریف نشده است. بنابراین آنها را تعریف میکنیم.
۱
۲
۳
۴
۵
۶
۷
|
public class PowerClass { public int Power ( int a , int b) { } } |
باز هم این تست رد میشود، چون کلاس و متد ایجاد شده خالی هستند و کاری انجام نمیدهند. گام بعدی نوشتن ساده ترین و کوتاه ترین کد ممکن برای پاس شدن این تست است، مطابق کد زیر:
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
|
public class PowerClass { public int Power ( int a , int b) { int p = 1; for ( int i = 1; i <= b; i++) p *= a; return p; } } |
اکنون تست پاس میشود، زیرا متد Power() با پارامترهای ۲ و ۱۰ مقدار ۱۰۲۴ را برگشت میدهد که با مقدار مورد انتظار برابر است. در این نقطه نیازمندی برنامه برآورده شده است.
همین که اولین تست پاس میشود، باید تستهای بیشتری را اضافه کنیم و کدهای لازم برای پاس شدن آنها را بنویسیم. این چرخه آنقدر ادامه مییابد تا تمامی نیازمندیهای برنامه برطرف شود. در ادامه مثالی واقعی از شیوه برنامه نویسی تست محور را بررسی خواهیم کرد.
همانطور که در مثال فوق مشاهده نمودید گردشکار TDD از دو گام تشکلیل شده است. در گام اول کد تست نوشته میشود که نتیجه مطلوب برنامه را طلب میکند. به این گام Red میگویند به این خاطر که با اجرای تست، تست رد میشود و نتیجه قرمز رنگ است. در گام دوم کدهای لازم برای برآورده شدن نتیجه مطلوب نوشته شده میشود تا تست پاس شود. به این گام Green میگویند، زیرا تست پاس شده است و نتیجه سبز رنگ است.
البته گام سومی هم وجود دارد که به آن Refactor میگویند. در این گام کدهای برنامه بهبود مییابند تا مطابق با اصول و قواعد طراحی باشند. البته عملکرد بیرونی کد نباید تغییر کند و باعث رد شدن تست قبلا پاس شده نشود.
[۱] برای نوشتن این تست از فریم ورک NUnit استفاده شده است که در فصل بعد به طور کامل تشریح میگردد.
شاید به نظر برسد که روش TDD سربار زیادی در برنامه نویسی ایجاد میکند و حجم کدها را دو برابر میکند. زیرا باید برای هر نیازمندی، ابتدا کدهای تست نوشته میشوند و پس از آن کدهای برنامه برای پاس کردن آنها ایجاد میگردند. ولی باید بدانیم که TDD مزایای بسیار مهمی را فراهم میکند که عبارتند از:
- کد کم حجم تر : کیفیت کد را از ابتدا تضمین میکند. برنامه سازان فقط کدی را به برنامه اضافه میکنند که تست ها را پاس کند و در نتیجه کد کمتر با احتمال خطای پایین تر خواهیم داشت.
- طراحی بهتر : چه از روی تفکر طراحی یا به عنوان اثر جانبی از TDD ، اکثر برنامه نویسان با سبک TDD قواعد SOLID را رعایت میکنند.
- تطبیق کامل کد با نیازمندی بیزنس : وقتی که نیازمندی به صورت یک تست نوشته شود، کدی که آن تست را برآورده میکند با احتمال بسیار زیاد آن نیازمندی را نیز براورده خواهد نمود.
- طراحی کتابخانه های ساده تر و متمرکز تر را فراهم میکند. به خاطر اینکه برای اکثر کلاسهای برنامه Interface ساخته میشود و شما اولین تست کننده کلاس خود هستید، توانایی طراحی کتابخانه مناسب را در شما به وجود میاورد.
- تقویت ارتباط بین برنامه ساز و کاربر بیزنس : به خاطر اینکه تست ها بیان کننده نیازمندی های کاربر بیزنس است، وی می تواند در فرآیند توسعه نرم افزار شریک شود.
- کد های بدون استفاده را از برنامه بیرون نگه میدارند. ممکن است برخی برنامه سازان کدهایی که احتمالا برای رفع نیازمندی های بعدی است را بنویسند که بعدا مورد نیاز نباشد با TDD چنین مشکلی پیش نمی آید.
- TDD امکان تست رگراسیون را فراهم میکند. شما به محض نوشتن یک تست و پاس کردن آن می توانید کل تست ها را اجرا کنید تا مطمئن شوید که کد جدید اثر جانبی بر سایر بخشهای برنامه نداشته است.
- از برخورد با خطاهای تکراری پیشگیری می کند. وقتی که یک تست برای یک باگ ظاهر شده در برنامه مینویسید، پس از هر بار اجرای تستها می توانید مطمئن شوید که دیگر چنین باگی رخ نمی دهد.
- معماری با کیفیت با امکان توسعه پذیری و تغییر پذیری : هنگام برنامه سازی با تفکر تست گر در انتهای برنامه ای که با کمک DI مستحکم است به راحتی قابل تغییر است، و نسبت به خطاها مقاوم است.
یکی از مشکلات TDD این است که تستها تنها برای توسعه دهندگان قابل درک است و افراد بیزنس نمیتوانند از آن سر در بیاورند. همانطور که قبلا بیان شد هر تست بیانگر یک نیازمندی است که با پاس شدنش، آن نیازمندی برطرف میشود. بنابراین اگر تستها برای افراد بیزنس قابل درک باشند، میتوانیم از آنها برای طراحی و نظارت بر تستها استفاده کنیم.
برای رسیدن به این هدف روش توسعه مبتنی بر رفتار (Behavior Driven Development) مطرح شده است. BDD یکی از مشتقات TDD است که بر اساس همکاری نزدیک تیم توسعه و تیم بیزنس برای ساخت نرم افزار با کیفیت عمل مینماید. در BDD به جای عبارت تست از مشخصه (Specification) استفاده میگردد. این روش توسعه از الگوریتم زیر پیروی می کند :
- نیازمندیهای بیزنس به شکل رفتارهای نرم افزار تعریف میگردند و هر رفتار به مجموعهای از مشخصههای قابل اجرا تبدیل میشوند. مشخصهها به زبان بیزنس بوده و برای همه قابل فهم است.
- کدهای مناسب برای پاس کردن مشخصهها نوشته میشوند.
- با اعمال قواعد Refactoring کدهای برآورده کننده مشخصهها بهینه میشوند.
- هر تغییر یا نیازمندی جدید به صورت یک رفتار در نرم افزار ثبت شده و مراحل فوق برای آن تکرار میشود.
منبع: agiledevelopment