После использования Automapper, чтобы сопоставить ViewModel, как и что я должен проверить?

StackOverflow https://stackoverflow.com/questions/3082179

Вопрос

Я пытаюсь проверить 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.

Все это сказало, что я обеспокоен тем, что я должен быть тестированием.

  1. Кажется, самоуверенно бросить result.ViewData.Model as TestCustomerForm. Отказ Это действительно проблема? Это касается меня, потому что в этом случае я не по-настоящему не делаю тестируемое развитие, и кажется, что я рассчитываю на конкретную реализацию, чтобы удовлетворить тест.
  2. Есть ли более подходящие тесты, чтобы обеспечить правильное отображение?
  3. Я должен провести тестирование каждого сопоставленного свойства от TestCustomerForm?
  4. Есть ли более общие контроллерные тесты действий, которые я должен делать?
Это было полезно?

Решение

Это одна из причин, по которым мы перемещаем 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top