۳۰ روز با TDD: روز اول - TDD چیست و چرا باید از آن استفاده کنم؟
داستان چیه؟
سپتامبر سال گذشته آقای James Bender در وبلاگهای تلریک یک مجموعه نوشته منتشر کرد به نام 30 روز با TDD. من میخوام یک ترجمه آزاد از این نوشتهها براتون داشته باشم تا با هم درباره Test Driven Development بیشتر بدونیم. اگر نمیدونید داستان TDD چی هست نگران نباشید، این مجموعه نوشتهها برای همین هست که با این روش بیشتر آشنا بشیم ;)
در هر نوشته، لینکی به مطلب اصلی هم خواهم داد که اگر خواستید متن رو به زبان انگلیسی مطالعه کنید راحت باشید و ممکنه در بعضی نوشتهها علاوه بر متن اصلی (که معمولاً خلاصه میشه و نکات مهمش ترجمه میشه) مطالب اضافهتری که از تجربه خودم یا دیگران هست رو هم اضافه کنم. این اولین نوشته از مجموعه نوشتههای مربوط به TDD هست.
#30RoozTDD
در توییتر با هشتگ 30RoozTDD# میتونید مطالب مرتبط به این سری نوشتهها رو پیدا کنید. خود آقای James Bender خوانندگان رو تشویق کرده بود که با هشتگ 30DaysOfTDD# مطلب هر روز رو توییت کنند تا شانس برنده شدن یک لایسنس رایگان نرمافزارهای تلریک مثل JustMock یا JustCode رو داشته باشند. من هم از ایده مشابه حمایت میکنم، حرکت 30 روز با TDD برای ارتقاء دانش خودم و همه دوستان و همکاران برنامهنویسم هست و اگر کسی مایل هست از این حرکت حمایتی بکنه، میتونه با من در تماس باشه.
و اما اولین روز: TDD چیست و چرا باید ازش استفاده کنم؟
نوشته اصلی را در این آدرس میتوانید مطالعه کنید، سعی من این هست که فقط بخشهای مهم و مفید رو ترجمه کردم بنابراین به عنوان مثال در این نوشته، بخش مقدمه رو ترجمه نکردم.
TDD چیست؟
قبل از اینکه وارد بحث اینکه TDD دقیقاً چیست و چطوری کار میکند بشویم لازم است یک درک مشخص و مشترک از مفهوم Unit Test (آزمون واحد) داشته باشیم. آزمون واحد نوع خیلی خاصی از تست نرمافزار، با اهداف و مجموعه ویژگیهای خیلی روشن است. خوشبختانه تعریف اولیه آزمون واحد، نسبتاً خیلی ساده است: Unit Test یک تست است که یک****نیاز مشخص برای یک****متد مشخص را آزمایش میکند. بعداً در این سری نوشتهها به مفاهیم مربوط به row tests و series tests و set tests خواهیم پرداخت ولی در نهایت همه اینها unit test هستند و اگر درست پیاده شوند در قانون یک نیاز/ یک متد میگنجند. توجه داشته باشید که اگر تست شما، قانون یک نیاز/یک متد را نقض کند، همچنان یک تست است اما دیگر unit test نیست.
آزمونهای واحد که قانون یک نیاز/یک متد را رعایت میکنند، ویژگیهای زیر را هم به عنوان یک unit test دارند:
- Targeted: آزمونهای واحدی که یک چیز (شامل مجموعهای از ورودیها) را در یک زمان آزمایش میکنند، هدفگیریشده هستند.
- Isolated: کدی که در حال تست آن هستید باید از کد اصلی برنامه و وابستگیهای خارجی یا رویدادها جدا بوده و ایزوله شده باشد.
- Repeatable & Predictable: یک آزمون واحد باید قابلیت بارها تکرار مجدد را داشته باشد (Repeatable باشد) و با فرض اینکه کد در حال تست و خود تست تغییر نکنند، هر دفعه همان نتیجه را تولید کند (قابل پیشبینی یا Predictable باشد)
- Independent: این آزمونها باید مستقل باشد، به صورت کلی هیچ تضمینی در خصوص ترتیب اجرا unit test ها وجود ندارد و بنابراین تستهای نوشته شده توسط شما نباید انتظار یا نیاز به این مساله داشته باشند.
درباره این ویژگیها در مطالب بعدی، جزئیات بیشتری را مطرح میکنیم. در روند ادامه این سری نوشتهها، خواهید دید که چطور میشود unit test واقعی نوشت و این تستها چه تفاوتی با سایر تستها دارند.
Test DRIVEN Development
بیشتر برنامهنویسهایی که کار با آزمونهای واحد را شروع میکنند، ابتدا کد برنامهشان را مینویسند و بعد unit test ها را. این گام مشترک و حتی میتوان گفت اولین گام منطقی برای ورود به دنیای TDD و unit testing است. بالاخره نمیشود یک تست نوشت وقتی چیزی برای تست کردن وجود ندارد. خیلی از این برنامهنویسها این کار را با یک روش و نیت خوب شروع میکنند: حتماً بعد از نوشتن کد، تست مربوط به کد را هم مینویسند. آنها نوشتن تستها را فراموش نمیکنند یا به خاطر یک کار مهم دیگر به تاخیر نمیاندازند.
اما در واقعیت، تعهد به نوشتن تست کار بسیار دشواری است و تقریباً همه برنامهنویسها بعد از مدتی دچار TED یا Test Eventually Develpoment میشوند و در واقع میگویند که بالاخره یک روزی تستش میکنیم و تا آن یک روزی ممکن است زمان زیادی طول بکشد یا حتی هرگز فرا نرسد!
اولین D در TDD مخفف Driven هست. ایده این روش این است که اولین کاری که برنامهنویس انجام میدهد نوشتن تست بر اساس ویژگی مورد انتظار فعلی نرمافزار (specification) است که روی آن کار میکند. این تستها باید fail شوند چرا که قابلیتی که میخواهند آزمایش کنند هنوز به وجود نیامده است. در این شرایط کار برنامهنویس این خواهد بود که سادهترین کد ممکن را بنویسد یا تست pass شود. اگر نرمافزار امکانات مورد انتظار (specification) بیشتری دارد، تستهای بیشتری بنویسید و چرخه refactor و بهینه کردن کد را ادامه دهید. وقتی همه مشخصات نرمافزار تست داشتند و تستهایشان pass میشد نرمافزار شما آماده است. عرضهاش کنید!

تمرین کردن TDD به نظر ساده میرسد اما در حقیقت اجرا کردن TDD به معنی ایجاد تغییرات بنیادی در روشهایی هست که برنامهنویسها برای ورود به حوزه توسعه نرمافزار آموزش دیدهاند. اوایل ممکن است حتی کمی غیرطبیعی به نظر برسد اما در نهایت اغلب برنامهنویسها متوجه میشوند که آخرش همان کد نوشتن است با این تفاوت که قبل از کدنویسی، کدهای تست را مینویسیم.
چرا باید از TDD استفاده کنم؟
مزایای زیادی برای استفاده از روش TDD در توسعه نرمافزار وجود دارد. برخی از این مزایا واضح هستند و بعضی نه. شاید واضحترین مزیت این باشد که کد شما وقتی کاملاً منطبق بر نیازهای مورد انتظار نرمافزار نیست، مشکلات و باگهای کمتری خواهد داشت. یکی از انواع باگهایی که TDD میتواند به صورت کامل حذفشان کند، "باگهای زامبی" هستند: باگهایی که به نظر میرسد رفع شدهاند ولی چند build بعدتر دوباره ظاهر میشوند!
وقتی رسیدگی به یک باگ یا مشکل به یک TDD کار محول میشود، اولین کاری که انجام میدهد نوشتن یک تست جدید است که باگ را آشکار و تست را fail میکند. بعد از این کار، برنامهنویس روش عادی کار در TDD را دنبال میکند: آنقدر کد بنویس که تست مورد نظر pass شود و بقیه تستها هم همچنان pass شده باقی بمانند. وقتی این کار تمام شد با فرض اینکه شما شرایطی که باعث بروز مشکل شده را به درستی تست کنید، مشکل دیگر نباید در iteration های بعدی برنامه دیده شود. درباره این موضوع بیشتر صحبت خواهیم کرد.
مزیت دیگر استفاده از TDD بهبود کیفیت کد است.همانطور که گفته شد در TDD برنامهنویسها باید سادهترین کد برای pass شدن تستها را بنویسند: سادهترین و کوتاهترین کد که معمولاً کیفیت بیشتری دارد. همچنین این کدها خوانایی بیشتری دارند که باعث میشود نگهداری کد سادهتر شود.
مزیت دیگر استفاده از TDD حذف موثر کدهای مرده از برنامه شماست. کدهای مرده یا Dead Code کدهایی هستند که در برنامه هستند اما هیچ وقت اجرا نمیشوند. این کد ممکن است یک متد یا کلاس باشند که هیچ وقت فراخوانی یا ارجاع داده نشدند یا بخشی از یک شرط باشند که هیچ وقت محقق نخواهد شد.
با استفاده از TDD شما فقط کدهایی را مینویسید که برای pass شدن تست نیاز دارید. اگر تستها بر اساس نیازمندیهای نرمافزار باشند، هیچ کدی از برنامه نیست که اجرا نشود و کدهایی که با روش TDD ایجاد میشوند همیشه مورد استفاده قرار میگیرند. با این حال تغییرات در نرمافزار به مرور زمان ممکن است باعث شوند یک متد که امروز مورد استفاده قرار میگیرد فردا هیچ استفادهای نداشته باشد.
با مانیتور کردن کدها در TDD اگر کدی داشته باشید که در تستی مورد استفاده قرار نگرفته از دو حال خارج نیست: یا یک تست از دست شما در رفته یا آن کد یک کد مرده (dead code) است و باید حذف شود.
ادامه دارد...