После использования 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
? - Есть ли более общие контроллерные тесты действий, которые я должен делать?
Решение
Это одна из причин, по которым мы перемещаем Automatper в пользовательскую AcceptResult или ActionFilter. В какой-то момент вы только действительно хотите проверить, что вы сопоставляете Foo в Foodto, но не обязательно проверить фактическое отображение. Перемещая Automapper в границы слоя (например, между контроллером View), вы можете просто тестировать то, что вы говорите «Автоматифу».
Это похоже на тестирование визуализации. Вы не тестируете от контроллера, который был представлен, а скорее, что вы сказали 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 к этому типу езды назначения, не требуя фактического выполнения сопоставления.
РЕДАКТИРОВАТЬ:
Вот пример тестового фрагмента:
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);
}
}
И в вашем модульном тесте вы бы использовали свою любимую структуру издевательства, чтобы заглушить этот Mapper.