مقدمه ای بر Unit Test در جاوا و اندروید
از سری مقالات آموزش جاوا و اندروید, مبحث Unit Test را بر میگزینیم.
در ابتدا باید مفهوم Unit Testing را بررسی کنیم. Unit Test به کد هایی اطلاق میشود که برنامه نویس برای اجرای عملکرد خاصی در برنامه, مینویسد تا آن قسمت از برنامه و رفتارش را بررسی کند. توجه داشته باشید که Unit test بر روی قسمت کوچکی مانند یک کلاس یا یک متد اعمال میشود. Dependency های خارجی باید از unit testing حذف شوند. برای مثال میتوان با استفاده از Mocking این کار را انجام داد. Unit Testing, این اطمینان خاطر را برای برنامه نویس ایجاد میکند که صحت و سقم برنامه را درفاز Logic بررسی کرده است.
همانطور که میدانیم یک متد, در دو حالت باید Test شود. در حالت اول, صحت متغیر های ارسالی و در حالت دوم صحت خروجی متد بررسی میشود. واضح است که اگر متدی متغیر های درستی را دریافت کند و نتیجه درستی را برگرداند, تضمینی بر درست نوشته شدن آن است. حالت اول که بررسی صحت متغیر های ارسالی است, Behavior Test و یا Interaction Test گفته میشود. در این حالت بر روی خروجی, اعتبارسنجی انجام نمیشود. حالت دوم, اعتبارسنجی خروجی متد است. این حالت را State Test گویند. برای مثال برای بررسی یک الگوریتم, از State Test استفاده میشود.
گاهی ممکن است چندین Component با یکدیگر ترکیب شوند و یک Component Group تشکیل دهند. این نوع Testing با هدف تعامل Component ها با یکدیگر انجام میشود. این نوع Testing کارکرد تمام سیستم را مورد بررسی قرار میدهد. در واقع یک کارکرد یک Component به تنهایی مفهوم ندارد و نتیجه آن در ارتباط با سایر Component ها معنا پیدا میکند. به این نوع Test ها, Integration Test و یا Functional Test گویند. همچنین بعضی Test ها در قالب Performance Testing است و مشخص میکند که اجرا این کد در High load, به میزان کافی سریع است.
کد هایی که برای Unit Test نوشته میشوند, در آدرس
src/test/java
قرار میگیرند.
مفهوم Mocking
اکنون به مبحث Mocking میپردازیم. اگر واژه Mock را در فرهنگ لغت انگلیسی جستجو کنید, مفهوم “something made as an imitation” را مشاهده خواهید کرد. در واقع میتوان اینطور تعریف کرد که Mocking به عملی تلقی میشود که یک چیز را به عنوان بدل ایجاد کنیم. Mocking در Unit Testing استفاده میشود. در هنگام Test یک Object, ممکن است این Object به Object های دیگری وابسته و برای پردازش به مقادیری که این Object ها بر میگردانند, نیاز داشته باشد. در واقع هدف از Mocking این است که میخواهیم کد ها را بدون دخالت Dependency های آنها اجرا کنیم. هدف این است که Mocked Object ها, نقش Object های واقعی را ایفا کنند. متد و یا متد هایی در این Object با متغیر های معینی فراخوانی میشود و نتیجه مورد نظر را بر میگرداند.
برای مثال فرض کنید به یک متد که برای احراز هویت کاربر است, مقادیر نام کاربری و رمز عبور را ارسال میکنیم و سپس مشخص میکنیم که اگر مقادیر وارد شده, با مقادیر مورد نظر برابر بودند, سطح دسترسی خاصی را برگرداند. واضح است که این پردازش نیاز به ارتباط با پایگاه داده دارد. اما در اینجا تنها ورودی و خروجی متد را مشخص کردهایم و پردازش متد مد نظر ما نیست. همین امر باعث میشود که حتی اگر متد دارای Dependency های خاصی است, در Testing مشکلی ایجاد نکند.
انوع Mocking
Mocking به دو روش Proxy و Class Remapping انجام میشود. یک Proxy Object, جانشین یک Object واقعی (Real) میشود و رفتار آن را تقلید میکند. طبق عملکرد Proxy, انتظار میرود که یک رفتار رابطی داشته باشد. این رفتار رابطی میتواند در مواردی مانند شبکه, میان تعدادی از Object ها, فایل ها و یا منابعی که duplicate کردن آنها پر هزینه است, استفاده شود. این مفهوم با در نظر گرفتن Dependency های کلاس ها, معنا بهتری میپذیرد. وقتی که یک کلاس چندین Dependency دارد, ایجاد یک Proxy Object از آن که رفتار آن را شبیه سازی میکند, میتواند Testing را تسهیل ببخشد.
روش دوم, در Class Loader, کلاس ها را Remap میکند. در این روش ما یک Dependency داریم که با کلاس Dependency.class متناظر است. اکنون ما یک MockClass از این Dependency ایجاد میکنیم. به این ترتیب در Class Loader, کلاس Dependency,class به MockClass.class تبدیل میشود. این روش بسیار قدرتمند تر از روش Proxy و بسیار پیچیده تر است.
Framework های Jmockit و Power Mock with Mockito از روش class mapping و Framework های Mockito, Easy Mock و JMock از روش proxy based استفاده میکنند.
مفهوم Stubbing
روش دیگری نیز به نام Stubbing وجود دارد که در کنار Mocking قرار میگیرد. استفاده از Stubbing بسیار آسان است و هیچ Extra Dependency را در زمان Testing, دخالت نمیدهد. در واقع بخشی از کلاسی که مورد نیاز است را پیاده سازی میکنیم تا در زمان Testing بتوان از آن استفاده کرد.
برای درک بهتر این تفاوت به یک مثال میپردازیم. میتوانیم با استفاده از Stubbing یک پایگاه داده را Stub کنیم. علت این کار این است که انجام تراکنش اهمیت بیشتری نسبت به ماهیت تراکنش دارد. به این معنا که میخواهیم مطمئن شویم, اطلاعات توسط User Interface در پایگاه داده ذخیره میشود و یا میخواهیم دریافت اطلاعات از پایگاه داده را بررسی کنیم. در اینجا ماهیت اطلاعات اهمیتی ندارد و تنها ارسال و دریافت اطلاعات مهم است. اما اگر بخواهیم اطلاعات خاصی را در زمان مد نظر در پایگاه داده ثبت کنیم, نیاز است تا پایگاه داده را Mock کنیم.
به عنوان مثال بعدی, مثالی که در مقاله RESTful وجود دارد را در نظر بگیرید. وقتی میخواهیم با استفاده از UserService.getModels, مشخصات کاربران را دریافت کنیم, نیاز است تا به پایگاه داده متصل شویم و مقادیر مورد نیاز را دریافت کنیم. اما در اینجا تنها هدف ما بررسی صحت عملکرد API است. بنابراین میتوان یک کلاس DBStub ایجاد کرد و دستوراتی که برای تراکنش با پایگاه داده استفاده میشود را در قالب متد ها پیاده سازی کرد. مانند اینکه یک متد
public UserModel getModel(String username) { .... }
پیاده سازی کنیم ومقدار مورد نظر را برگردانیم. در اینجا از روش هایی مانند Hibernate استفاده نمیکنیم و درگیر مباحث پایگاه داده نمیشویم.
در این مقاله سعی بر این شد تا مفهوم Unit Testing و دو روش پرکاربرد Mocking و Stubbing را بررسی کنیم.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.