بعد استخدام Automapper لرسم خريطة ViewModel كيف وماذا يجب أن أختبر؟
-
28-09-2019 - |
سؤال
أحاول اختبار Index
عمل وحدة تحكم. يستخدم العمل السيارات لرسم خريطة مجال Customer
اعترض على نموذج العرض TestCustomerForm
. بينما يعمل هذا الأمر ، أشعر بالقلق إزاء أفضل طريقة لاختبار النتائج التي أتلقاها من Index
عمل.
يبدو أن إجراء فهرس وحدة التحكم مثل هذا:
public ActionResult Index()
{
TestCustomerForm cust = Mapper.Map<Customer,
TestCustomerForm>(_repository.GetCustomerByLogin(CurrentUserLoginName));
return View(cust);
}
و TestMethod
يشبه هذا:
[TestMethod]
public void IndexShouldReturnCustomerWithMachines()
{
// arrange
var customer = SetupCustomerForRepository(); // gets a boiler plate customer
var testController = CreateTestController();
// act
ViewResult result = testController.Index() as ViewResult;
// assert
Assert.AreEqual(customer.MachineList.Count(),
(result.ViewData.Model as TestCustomerForm).MachineList.Count());
}
في ال CreateTestController
الطريقة التي أستخدمها Rhino.Mocks
للسخرية من مستودع العملاء وإعداده لإعادة العميل من SetupCustomerForRepository
. وبهذه الطريقة أعلم أن المستودع سيعيد العميل المقصود عند Index
مكالمات العمل _repository.GetCustomerByLogin(CurrentUserLoginName)
. لذلك ، أنا أؤكد أن عدد متساوٍ مناسب للإرضاء IndexShouldReturnCustomerWithMachines
.
كل ذلك قيل إنني قلق بشأن ما يجب أن أختبره.
- يبدو من المفترض أن نلقي
result.ViewData.Model as TestCustomerForm
. هل هذه حقا مشكلة؟ هذا يهمني لأنني في هذه الحالة لا أقوم حقًا بتطوير الاختبار ويبدو أنني أعتمد على تطبيق معين لتلبية الاختبار. - هل هناك اختبارات أكثر ملاءمة لضمان الخرائط الصحيحة؟
- هل يجب أن أختبر كل خاصية تم تعيينها من
TestCustomerForm
? - هل هناك اختبارات عمل تحكم أكثر عمومية يجب أن أقوم بها؟
المحلول
هذا هو أحد الأسباب التي تجعلنا ننقل السيارات إلى ActionResult أو ActionFilter. في مرحلة ما ، تريد فقط اختبار أنك قمت بتعيين Foo إلى Foodto ، ولكن ليس بالضرورة اختبار الخرائط الفعلية. من خلال نقل السيارات إلى حدود الطبقة (مثل عرض وحدة التحكم) ، يمكنك مجرد اختبار ما تخبره بالسيارات للقيام به.
هذا مشابه للاختبار ViewResult. لا تختبر من وحدة تحكم تم تقديم طريقة عرض ، بل كنت قد أخبرت MVC تقديم مثل هذا العرض. تصبح نتيجة عملنا:
public class AutoMapViewResult : ActionResult
{
public Type SourceType { get; private set; }
public Type DestinationType { get; private set; }
public ViewResult View { get; private set; }
public AutoMapViewResult(Type sourceType, Type destinationType, ViewResult view)
{
SourceType = sourceType;
DestinationType = destinationType;
View = view;
}
public override void ExecuteResult(ControllerContext context)
{
var model = Mapper.Map(View.ViewData.Model, SourceType, DestinationType);
View.ViewData.Model = model;
View.ExecuteResult(context);
}
}
مع طريقة مساعد على فئة وحدة تحكم قاعدة:
protected AutoMapViewResult AutoMapView<TDestination>(ViewResult viewResult)
{
return new AutoMapViewResult(viewResult.ViewData.Model.GetType(), typeof(TDestination), viewResult);
}
الأمر الذي يجعل وحدة التحكم الآن الآن تحدد فقط ما يجب تعيينه من/من/إلى ، بدلاً من تنفيذ التعيين الفعلي:
public ActionResult Index(int minSessions = 0)
{
var list = from conf in _repository.Query()
where conf.SessionCount >= minSessions
select conf;
return AutoMapView<EventListModel[]>(View(list));
}
في هذه المرحلة ، أحتاج فقط إلى الاختبار ، "تأكد من قيامك بتخطيط كائن FOO هذا إلى هذا النوع من Foodto Destination" ، دون الحاجة إلى أداء التعيين بالفعل.
تعديل:
إليك مثال على مقتطف الاختبار:
var actionResult = controller.Index();
actionResult.ShouldBeInstanceOf<AutoMapViewResult>();
var autoMapViewResult = (AutoMapViewResult) actionResult;
autoMapViewResult.DestinationType.ShouldEqual(typeof(EventListModel[]));
autoMapViewResult.View.ViewData.Model.ShouldEqual(queryResult);
autoMapViewResult.View.ViewName.ShouldEqual(string.Empty);
نصائح أخرى
من المحتمل أن أفصل الاقتران بين AutoMapper
ووحدة التحكم عن طريق إدخال تجريد:
public interface IMapper<TSource, TDest>
{
TDest Map(TSource source);
}
public CustomerToTestCustomerFormMapper: IMapper<Customer, TestCustomerForm>
{
static CustomerToTestCustomerFormMapper()
{
// TODO: Configure the mapping rules here
}
public TestCustomerForm Map(Customer source)
{
return Mapper.Map<Customer, TestCustomerForm>(source);
}
}
بعد ذلك ، تقوم بتمرير هذا إلى وحدة التحكم:
public HomeController: Controller
{
private readonly IMapper<Customer, TestCustomerForm> _customerMapper;
public HomeController(IMapper<Customer, TestCustomerForm> customerMapper)
{
_customerMapper = customerMapper;
}
public ActionResult Index()
{
TestCustomerForm cust = _customerMapper.Map(
_repository.GetCustomerByLogin(CurrentUserLoginName)
);
return View(cust);
}
}
وفي اختبار الوحدة الخاص بك ، يمكنك استخدام إطار السخرية المفضل لديك لاستنباط هذا الخريطة.