C# enthusiast. NET foundation member
آموزش Unit Testing با استفاده از NUnit و Moq بخش دوم: Mocking
در این مقاله قصد داریم که به Mocking و کتابخانه محبوب Moq بپردازیم.
در این مقاله از GitHub Gist استفاده شده است و لود شدن قسمت مربوط به کد ها ممکن است کمی زمانبر باشد
تعریف Mock
معمولا Unit Test ها روی یک Unit کار میکنند که به یک کلاس اطلاق می شود. گاهی اوقات این Unit یک سری Dependency هایی را همراه خود دارند که جزئی از تست نیستند ولی Unit مربوطه بدون این dependency ها نمی تواند کار کند.سناریویی را در نظر بگیرید که ClassA سیستم تحت تست می باشد ولی این کلاس در داخل خود از ClassB استفاده می کند. حال ما نیاز داریم که ClassB را در تست خود جایگزین کنیم و تنها ClassA را تست کنیم. در اینجا باید از Test Double ها استفاده کنیم. یکی از Test Double ها Mock می باشد. Mock در واقع یک Fake Object می باشد که میتواند مقدار تعریف شده را به عنوان Result خود برگرداند.همچنین یک Mock می تواند شامل اطلاعات اضافی مانند اینکه چه پارامتر هایی مورد استفاده قرار گرفته و چند بار یک Mock Object صدا زده شده را برگرداند.
کتابخانه Moq
یک کتابخانه بسیار کاربردی در زمینه Mocking می باشد که قابلیت های بسیاری از جمله شبیه سازی متد ها،Event ها، پراپرتی ها و... را در اختیارمان قرار میدهد.
نصب Moq
به سراغ پروژه ای که در قسمت قبلی ایجاد کرده ایم می رویم. در Package Manager Console دستور زیر را برای نصب Moq اجرا میکنیم.
Install-Package Moq -Version 4.16.1
همچنین می توانیم از قسمت Manage Nuget Packages به دنبال Moq بگردیم و آن را نصب کنیم.
شروع کار با Moq
اینترفیس ILog زیر را در بگیرید.
این اینترفیس یک متد ساده با نام WriteMessage دارد. حال کلاس BankAccount را در نظر بگیرید که به این اینترفیس وابستگی دارد.
هدف ما تست یونیت BankAccount می باشد نه اینترفیس ILog. در اینجا کتابخانه Moq به کمک ما می آید.
در اینجا در متد SetUp یک Mock Object از اینترفیس ILog ساخته ایم و آن را به BankAccount پاس داده ایم. بوسیله آن نیازمندی یونیت BankAccount به ILog تامین می شود و می توانیم یونیت BankAccount را بدون نیاز به ساخت اینترفیس ILog تست کنیم. در ادامه با سایر قابلیت های Moq آشنا می شویم.
ایجاد Mocking Methods
اینترفیس IFoo زیر را در بگیرید. در ادامه مقاله قصد داریم که بدون نیاز به پیاده سازی این اینترفیس، آن را Mock کنیم.
ابتدا یک mock از اینترفیس IFoo می سازیم.
کلاس mock یک متد با نام Setup در اختیارمان قرار میدهد که به وسیله آن میتوانیم رفتار mock را تغییر دهیم. رفتار متد DoSomething را به شکل زیر تعریف میکنیم
خط 1 نشان میدهد که اگر متد هرکدام از مقدار Ping یا Foo را دریافت کرد False را به عنوان نتیجه بازگرداند و در خط 2 مقدار Pong توسط متد دریافت شد، مقدار True را بازگرداند. حال اگر متد DoSomething مقداری غیر از Ping، Pong و یا Foo را دریافت کند، نتیجه آن مقدار Default خروجی متد می باشد که در این حالت مقدار Default برای boolean مقدار False می باشد.
بررسی Mocking برای متدهای وابسته به مقدار ورودی
متد Add از اینترفیس IFoo را در نظر بگیرید. فرض کنید که میخواهیم وقتی که عدد زوج است، مقدار True را برگردانیم و یا وقتی که ورودی در رنج اعداد 1 تا 10 بود مقدار False را برگردانیم. ویا برای متد DoSomething این شرط را داشته باشیم که ورودی String باید شامل حروف باشد.کلاس It از Moq شامل چند متد است که بوسیله آن میتوانیم برای خروجی متد ها شرط بگذاریم.
بررسی Mocking برای پارامترهای Ref و Out
برای Mock کردن پارامتر out در Moq می توانیم به شکل زیر عمل کنیم
دقت کنید که در متد بالا تعریف کرده ایم که اگر ورودی TryParse مقدار Ping بود،خروجی متد مقدار True داشته باشد و out Argument را به مقدار ok تطبیق دهد.
برای Ref میتوانیم مانند زیر عمل کنیم.دقت داشته باشید که در مورد Ref کتابخانه Moq به صورت Reference Equality عمل میکند و Reference دو آبجکت را بررسی میکند.
بررسی مقدار بازگشتی برای Mocking متدها
هنگامی که مقدار بازگشتی متد باید دارای مقدار خاصی باشد میتوانیم از یک Lambda Expression برای خروجی مقدار Returns استفاده کنیم. به طور مثال مقدار ProcessString در Mock زیر همواره کاراکتر ها را lower کرده و برمیگرداند.
بررسی CallBack در Moq
موقع Setup کردن یک متد میتوانیم تعریف کنیم که به ازای هر Call مقدار های مختلف داشته باشیم. مثلا میتوانیم تعریف کنیم که هربار متد Call شد یک Counter مقدارش زیاد شود که میتوانیم از CallBack به شکل زیر استفاده کنیم. در واقع CallBack یک فانکشن است که هربار که متد فراخوانی میشود،مقدار تعریف شده در CallBack اجرا می شود.
بررسی Exception ها در Moq
برای اینکه یک Exception را در Moq شبیه سازی کنیم میتوانیم به صورت زیر عمل کنیم.
بررسی Property Mocking
در mock Object نمیتوان که مقداری را به یک Property تخصیص داد چرا که در اصل هیج Object ای وجود ندارد. میتوانیم به صورت زیر یک مقدار را به یک پراپرتی Assign کنیم.
در مورد Setter های یک پراپرتی می توانیم مانند زیر عمل کنیم و هربار که Setter یک پراپرتی فراخوانی شد، عملیاتی را انجام دهیم.
بررسی Value Tracking پراپرتی ها
میتوانیم به شکل زیر، تمامی پراپرتی های موجود در mock را طوری تنظیم کنیم که رفتاری مانند یک پراپرتی داشته باشند.
در خط 5 تمامی پراپرتی های ابجکت mock را تنظیم میکنیم که رفتاری مانند پراپرتی داشته باشند.
بررسی Event Mocking
فرض کنید که اینترفیس زیر را به عنوان نمونه داریم که دارای یک Event و یک Delegate می باشد.
حال یک کلاس با نام Doctor به شکل زیر ایجاد میکنیم که وابستگی به اینترفیس IAnimal دارد و به Event ها Subscribe کرده است.
برای Raise کردن یک Event می توانیم مانند زیر عمل کنیم.
میتوانیم در SetUp یک متد، Raise کردن یک Event را در نظر بگیریم.
میتوانیم یک Custom Event داشته باشیم. به طور مثال در اینترفیس IAnimal یک delegate داریم که میتوانیم از آن به شکل زیر در mock object استفاده کنیم.
بررسی دسترسی به Protected Members در Mock
فرض کنید که کلاس Person را به شکل زیر داریم که دارای اعضای Protected می باشد.
میتوانیم به شکل زیر به اعضای Protected دسترسی داشته باشیم و همچنین متدهای Protected را Invoke کنیم.
دقت داشته باشید که که برای Invoke کردن متد به جای استفاده از دستور It باید از دستور ItExpr استفاده کنیم.
نتیجه گیری
در این مقاله با Mocking و کتابخانه Moq آشنا شدیم. به طور کلی کتابخانه Moq ابزاری فوق العاده قدرتمند برای شبیه سازی بخش هایی از یونیت که مورد نظر تست نیستند می باشد. اگر به کد های این بخش نیاز داشتید میتوانید آن را از گیت هاب به لینک زیر دریافت کنید. خوشحال میشوم که نظرات شما را راجب این مقاله بدانم و همچنین اگر دوست داشتید میتوانید من را به یک قهوه مهمان کنید.
مقالات بیشتر در دات نت زوم
مطلبی دیگر از این انتشارات
ساخت چت روم با Blazor Web Assembly و SignalR قسمت دوم: ساخت کلاینت
مطلبی دیگر از این انتشارات
C# 9.0: Records - کار با دادههای تغییر ناپذیر کلاسها
مطلبی دیگر از این انتشارات
C# 9.0: init-only - ایجاد خصوصیات تغییر ناپذیر بدون سازنده