اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
چهار دقیقه
شاید شما هم در پروژه خودتان نیاز داشته باشید تا اتصالات MediatR را بررسی یا به نوعی از صحت کدهایی که بر پایه MediatR زدید مطمئن شوید. در اینجا به بررسی نحوه Assert کردن اتصالات MediatR میپردازم. اول از همه باید به این فکر کرد که چه چیزی را میخواهیم تست کنیم؟ طبیعتا وقتی یک Command را ایجاد میکنیم، انتظار داریم که یک CommandHandler مختص به آن نیز ایجاد شده باشد. پس ما به بررسی ساختار کد میپردازیم.
همانطور که مشخص است، برای دو پارامتر ورودی، مقادیر پیش فرض Command و CommandHandler در نظر گرفته شدهاند. توجه داشته باشید این اسم کامل نیست و تنها نام انتهای کلاس است: برای مثال OrderCommand/OrderCommandHandler:
در مرحلهی بعدی، تمام Handlerها را پیدا و لیست میکنیم. در اینجا نیز مانند کد بالا، اگر لیست خالی باشد، به این معناست که هیچ handler ای تعریف نشدهاست.
مرحله بعدی، بررسی کردن تعداد Commandها و CommandHandlerها میباشد. طبیعتا باید مقدار این دو برابر باشند؛ چرا که هر Command، نیاز به یک CommandHandler نیز دارد. بنابراین اگر تعداد این دو یکسان نبود، ساختار ما درست نیست؛ پس مقدار false برگشت داده میشود :
بعد از تمام این ماجراها، به مرحله آخر میرسیم؛ آن هم چک کردن نظیر به نظیر Commandها با CommandHandlerها میباشد. به این صورت که اگر به ازای هر Command تعریف شده یک CommandHandler با GenericTypeArguments از نوع command وجود داشته باشد، میتوان گفت که ساختار ما درست تعریف شدهاست.
توجه داشته باشید برای تست کردن قسمتهایی مثل Query و Notification نیز میتوان از همین فرآیند بهره برد؛ البته با کمی تغییر در پیاده سازی متد IsValid.
برای شروع، یک Interface را با متد IsValid را ایجاد میکنیم. این Interface بعدها توسط کلاس CommandValidator پیاده سازی میشود تا هر وظیفه، تشخیص درست بودن اتصالات command را چک کنند.
همانطور که مشخص است، متد IsValid دو پارامتر اسم ارسال کننده (Command,Query,Notification) و اسم Handler این ارسال کنندهها را دریافت میکند.
internal interface IValidation { bool IsValid(string sendNamesEndTo, string handlerNamesEndTo); }
کد زیر نحوه پیاده سازی IValidation را برای کلاس CommandValidator، نشان میدهد:
public bool IsValid(string commandNamesEndTo = "Command", string commandHandlersEndTo = "CommandHandler") { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var commandTypeInfos = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandNamesEndTo.ToLower()) && typeInfo.ImplementedInterfaces.Any(type => type == typeof(IBaseRequest)))); var memberInfos = commandTypeInfos as TypeInfo[] ?? commandTypeInfos.ToArray(); if (!memberInfos.Any()) throw new ArgumentException("Can not find any Command"); var handlerTypeInfo = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandHandlersEndTo.ToLower()))); var typeInfos = handlerTypeInfo as TypeInfo[] ?? handlerTypeInfo.ToArray(); if (!typeInfos.Any()) throw new ArgumentException("Can not find any CommandHandler"); if (typeInfos.Count() != memberInfos.Count()) return false; return !(from typeInfo in memberInfos let interfaces = typeInfos.SelectMany(x => x.ImplementedInterfaces) where interfaces.Any(x => x.GenericTypeArguments.All(type => type != typeInfo)) select typeInfo).Any(); }
public bool IsValid(string commandNamesEndTo = "Command", string commandHandlersEndTo = "CommandHandler")
داخل بدنه متد، ابتدا تمام Assemblyهای موجود در App را لیست میکنیم :
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
سپس تمام کلاسهای تعریف شدهای را که از IBaseRequest ارث بری کرده و انتهای نام آنها شامل commandNamesEndTo باشد، لیست میکند. میتوان گفت تا اینجا تمام Commandها را پیدا کردهایم. در صورتیکه لیست موجود خالی باشد، یعنی یکجای کار مشکل دارد؛ شاید کلا Command ای تعریف نشده یا ... پس یک ArgumentException را throw میکنیم.
var commandTypeInfos = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandNamesEndTo.ToLower()) && typeInfo.ImplementedInterfaces.Any(type => type == typeof(IBaseRequest)))); var memberInfos = commandTypeInfos as TypeInfo[] ?? commandTypeInfos.ToArray(); if (!memberInfos.Any()) throw new ArgumentException("Can not find any Command");
var handlerTypeInfo = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandHandlersEndTo.ToLower()))); var typeInfos = handlerTypeInfo as TypeInfo[] ?? handlerTypeInfo.ToArray(); if (!typeInfos.Any()) throw new ArgumentException("Can not find any CommandHandler");
if (typeInfos.Count() != memberInfos.Count()) return false;
return !(from typeInfo in memberInfos let interfaces = typeInfos.SelectMany(x => x.ImplementedInterfaces) where interfaces.Any(x => x.GenericTypeArguments.All(type => type != typeInfo)) select typeInfo).Any();
در نهایت برای استفاده میتوان به شکل زیر کدها را استفاده کرد :
var validCommandConfiguration = new CommandValidator().IsValid();
برای دیدن کدهای کامل پیاده سازی تست Command/Query/Notification، میتوانید از لینک گیت هاب زیر استفاده کنید.