آشنایی با TDD

TDDبنا به دلایلی ما از Test automation استفاده می کنیم.اولین موضوع، کیفیت طراحی ما است. وقتی وارد فضای تست نویسی به خصوص TDD می شویم، می بینیم که تست به طور مستقیم بر روی طراحی ما تاثیر می گذارد. در مواردی مثل Coupling بین کامپوننت ها، dependency، رفتارها و یا encapsulate کردن، که بر روی تست تاثیر گذار هستند و می توانند ما را به سمت طراحی بهتر هدایت کنند.موضوع دوم، باگ های کمتر و عدم ظهور مجدد باگ ها می باشد.موضوع سوم Tests as Specification است. وقتی برای شروع بر روی نوشتن تست ها تمرکز می کنیم، در واقع تمرکز ما بر روی رفتار SUT (System Under Test) است. به این صورت که قبل از ساختن SUT، می توانیم در مورد رفتار آن فکر کنیم و حالت های مختلف آن را در نظر بگیریم و آن را به صورت گام به گام حل کنیم. موضوع چهارم Defect Localization است و به موضوع پیدا کردن باگ ها می پردازد که می تواند هزینه بالایی برای برنامه نویس ها داشته باشد. موضوع پنجم تست به عنوان مستندات است. بهترین نمونه کدهایی که برای libraryها می توانیم در نظر بگیریم، استفاده از کدهای ما در تمام حالت های ممکن به عنوان یک sample code است. موضوع ششم Tests as Safety Net است. برای مثال کدهایی که می نویسیم و جرأت تغییر دادن آن را پیدا نمی کنیم. به این علت که از side effectهای آن تغییرات مطمئن نیستیم. تست ها امکان بهینه کردن کدها را بدون آسیب زدن به ماژول های دیگر فراهم می کنند.در ادامه به سطوح مختلف تست می پردازیم. مفهومی به نام Unit Test وجود دارد که بر روی اجزای کوچک و مستقل از دنیای بیرون تمرکز می کند.همچنین مفهومی به نام Integration Test نیز وجود دارد که به دنبال تست ارتباطات و یکپارچکی اجزای مستقل از هم می باشد. ممکن است با حالت هایی روبرو شویم که نرم افزار ما باگ ندارد اما برای مشتری نیز قابل قبول نیست. بنابراین وارد فضای Acceptance Test می شویم. در این مفهوم از نگاه کاربر، تست هایی انجام می شود که آیا functionality پیاده سازی شده مورد قبول ذینفع آن می باشد یا خیر. در ادامه به بررسی Terminologyهای تست می پردازیم.مفهوم SUT یا System Under Test، به جزئی گفته می شود که ما برای آن تست می نویسیم.مفهوم Fixture، به موضوع فراهم کردن شرایط انجام یک تست می پردازد. بنابراین به هر آنچه که باید وجود داشته باشد تا بتوانیم یک SUT را به طریقی که می خواهیم فراخوانی کنیم، fixture گفته می شود.مفهوم Test Method، به این موضوع می پردازد که هر تست را در قالب یک متد می نویسیم که آن متد یک رفتار از SUT را verify می کند. مفهوم Testcase Class، مجموعه ای از test methodهای مرتبط را در کنار هم می گذارد.ابزار Test Framework برای مثال xUnit، به ابزاری می گویند که به ما اجازه می دهد تست بنویسیم و از طریق Test Runner که معمولا به صورت built-in در فریم ورک ها وجود دارد، می توانیم تست ها را اجرا کنیم.در حوزه terminology، هر تستی دارای چهار گام می باشد.مرحله Fixture Setup در راستای فراهم کردن شرایط اجرای تست انجام می شود. مرحله Exercise SUT به منظور اجرای SUT از طریق API مربوطه آن انجام می شود.مرحله سوم عملیات Verification را انجام می دهیم تا از نتیجه کاری که انجام داده ایم مطمئن شویم. و مرحله Tear down کردن به معنای از بین بردن آثار تست در آن محیط است.در ادامه نگاهی به چرخه TDD خواهیم داشت و به تعریف های پیرامون آن می پردازیم.مفهوم TDD، یک رویکرد test first برای توسعه نرم افزار است که به ما کمک می کند در یک مسئله بزرگ، در گام های کوچک تست بنویسیم، functionality آن را پیاده سازی کنیم و آن را بهینه کنیم. به این ترتیب مسیر فرآیند توسعه را از طریق نوشتن تست ها مشخص می کنیم.رویکرد test first به معنای نوشتن تست ها قبل از توسعه چه مزیت هایی می تواند به همراه داشته باشد؟چرخه TDD به این صورت مطرح می شود که ما شروع به نوشتن یک تست از یک رفتار سیستم می کنیم. از آنجایی که هنوز کدی وجود ندارد که آن تست را satisfy کند، بنابراین تست در وضعیت قرمز قرار می گیرد. بعد از آن شروع به نوشتن حداقل کدی می کنیم که بتواند آن تست را پاس کند. بعد از پاس شدن تست، می توانیم یک گام به سمت بهینه کردن کد و تست آن برداریم. سه قانون در TDD مطرح می شود.شما باید قبل از نوشتن production code، یک failing test برای آن نوشته باشید.زمانی که unit test در وضعیت قرمز قرار دارد، نوشتن تست را ادامه ندهید و بعد از درست کردن کد می توانید تست را ادامه دهید.به محض سبز شدن کد، functionality بیشتری اضافه نکنید.یکی از سوال های رایجی که در این حوزه وجود دارد این است که چرا باید ابتدا یک failing test بنویسیم و بعد با حداقل کد آن را پاس کنیم و بعد refactor کنیم. آیا نمی شود همان ابتدا یک کد خوب نوشت؟قانونی به نام Tow Hats در refactoring وجود دارد که به این موضوع اشاره می کند. developerها دو کلاه می توانند روی سر خود داشته باشند. کلاه کابوی که وقتی روی سر آن ها باشد، به رفع باگ و اضافه کردن feature جدید می پردازند. یا کلاه آتش نشانی که به refactoring اشاره می کند. یعنی کد را تغییر می دهند اما functionality تغییر پیدا نمی کند. این قانون می گوید که شما به عنوان برنامه نویس، این دو کلاه را نباید با هم روی سر خود بگذارید. به این دلیل که هیچ کدام را درست انجام نمی دهید. در TDD، رویکرد این است که در کمترین زمان به جواب درست برسیم. بنابراین ساده ترین کدی که به جواب برسد را می نویسیم و برای refactoring با توجه به وجود تست ها، هر جا گام اشتباهی بردایم تست ها fail می شوند.شاخص های کیفی یا Quality Attributeهایی که برای تست های خود در نظر می گیریم را می توانیم در سه مورد دسته بندی کنیم.مفهوم Readability به این منظور که تست را بتوانیم به خوبی درک کنیم.مفهوم Maintainability به این منظور که نگهداری تست ها با هزینه کمی همراه باشد.مفهوم Trustworthiness که به منظور بالا بردن اعتماد پذیری تست ها در نظر گرفته می شود.منابع:xUnit Test Patterns - Martin FowlerUnit Testing Principles, Practices, and Patterns - Vladimir KhorikovEffective Unit Testing for Java developersGrowing Object-Oriented Software, Guided by Tests