مشاور و مدرس برنامه نویسی در حوزه دات نت - https://github.com/mjebrahimi
نکاتی در مورد تست نویسی روی EF6/EFCore توسط دیتابیس InMemory
یکی از مزیت های الگوی Repository، قابلیت تست پذیری لایه دیتا به واسطه ساختن ریپازیتوریهای Fake هست. در واقع ریپازیتوریهایی میسازیم که از (مثلا IRepository) ارثبری میکنه ولی به جای ذخیره سازی در بانک اطلاعاتی، دیتاها رو به صورت InMemory ذخیره و واکشی میکنه.
همچنین روش های دیگری برای اینکار وجود داره مثل Mock کردن DbContext یا DbSet که هر کدوم دردسرها و محدودیتهای خودشون رو داره تا جایی که حتی بخشیدن عطاش به لقاش منطقی تره.
اینجا لیستی از بهترین منابعش رو گلچین کردم 1 و 2 و 3 و 4 و 5 و 6 و 7 و 8 و 9 تا واسه خودم هم آرشیو بمونه.
توی EFCore به دلیل وجود پروایدر InMemory نیازی به این کار نیست و عمل تست نویسی رو برامون خیلی راحت کرده ولی توی EF6 چون پروایدر InMemory نداریم مجبوریم تن به یکی از اینها بدیم.
پروژه سورس باز و رایگان Effort یک پروایدر InMemory مخصوص Entity Framework هست (که از نسخه های 5 و 6 EF پشتیبانی میکنه) و امکان UnitTest نویسی برای EF رو براحتی براتون فراهم میکنه و سعی کرده است.
این کتابخونه از برای دیتابیس خودش از NMemory استفاده میکنه که یک Engine دیتابیس رابطه ای InMemory هست و سعی کرده تا حد زیادی رفتارهای یک دیتابیس واقعی رو شبیه سازی کنه و از مواردی از جمله Indexes و Foreign Key Relations و Transaction Handling and Isolation و Stored Procedures و ... پشتیبانی میکنه پس به نسبت بقیه روش ها (مثل یه List استاتیک!) در مورد شبیه سازی دیتابیس، رفتار بسیار بسیار قابل اعتمادتری ارائه میده.
کار باهاش هم خیلی راحته و از لینک و دردسرها و محدودیتهای پیادهسازی روشهای قبلی رو نداره:
Entity Framework Effort Overview
واسه مطالعه بیشتر هم لینک های زیر خوبن:
- Save time mocking – use your real Entity Framework DbContext in unit tests
- Two strategies for testing Entity Framework - Effort and SQL CE
- Using Effort - Entity Framework Unit Testing Tool
نکته:
تمام روش های بالا و اساسا تمام دیتابیسهای InMemory (حتی پروایدر InMemory خود EFCore) یه مشکل اساسی دارن و اون هم اینه که هیچ کدوم نمیتونن 100 درصد رفتار یک دیتابیس واقعی رو شبیه سازی کنن. بدیهی هم هست چون که هیچ کدوم نمیتونن تمام قابلیت های دیتابیس واقعی پروژه شما (مثلا SqlServer) رو داشته باشن.
این کمبودها که تعدادشون هم کم نیست بعضی مواقع باعث مشکل میشن.
مثلا در مورد دیتابیس InMemory خود EFCore :
- شما نمیتونین SP های خودتون رو روش اجرا کنین
- شما نمیتونین از Transaction های دیتابیسی استفاده کنین
- شما نمیتونین از Function های دیتابیسی و یا کلا هر قابلیت منحصر به دیتابیس تون استفاده کنین
- قیودی که فقط توی دیتابیس واقعی اعمال میشن و ...
- حتی یک کوئری یکسان روی InMemory و دیتابیس واقعی میتونه نتایج متفاوتی داشته باشه (بدلیل تفسیر متفاوتی ازش توسط پروایدر مربوطه انجام میشه)
- در واقع تست درون حافظهی LINQ to Objects با تست واقعی LINQ to Entities که روی یک بانک اطلاعاتی واقعی اجرا میشود، الزاما نتایج یکسانی نخواهد داشت
- حتی اگه یه متدی که معادل SQL ایی نداره توی کوئری هاتون استفاده کنین، هنگام استفاده از InMemory خطا نمیده ولی موقع دیتابیس واقعی خطای عدم امکان تفسیر به معادل Sql میده
در نتیجه همه اینها پاس شدن یک تست با دیتابیس InMemory الزاما دلیل بر صحت عملکرد پروژه و به معنای درست کار کردن برنامه در دنیای واقعی نیست و ممکنه همون تست با دیتابیس واقعی به خطا بخوره.
در نهایت هرچند که دیتابیس InMemory رفتار قابل اطمینانی از یه دیتابیس رو نمیتونه شبیه سازی کنه ولی در مورادی که به این تناقض ها بخورد نمیکنیم (معمولا در حد CRUD و یه Storage) میتونه خیلی مفید و کاربردی باشه. فقط نکته اش اینه که حواسمون به این کمبود ها باشه و توصیه میشه که حتما در این گونه موارد که از Integration Test به همراه یک دیتابیس واقعی استفاده کنیم.
نظر شما در این باره چیه؟ توی پستهای بعدی توضیحات بیشتری خواهم داد.
مطلبی دیگر از این انتشارات
عیب یابی و رفع مشکلات پرفرمنسی برنامه های دات نتی
مطلبی دیگر از این انتشارات
آموزش Microservices در ASP.NET Core (سری دوم)
مطلبی دیگر از این انتشارات
آموزش Microservices در ASP.NET Core (سری اول)