Модульное тестирование служб приложений ASP.NET MVC, использующих AutoMapper и преобразователи типов - PullRequest
1 голос
/ 09 августа 2011

Сервисный уровень моего приложения ASP.NET MVC 3 использует AutoMapper для сопоставления моделей представлений с бизнес-объектами.Я реализовал преобразователи типов для преобразования идентификаторов объектов, представленных в запросе post / get, в бизнес-объекты. Преобразователи типов выполняют поиск сущностей в базе данных по идентификаторам и перестраивают бизнес из них.Например:

class UserViewModel {
   // user fields
   Guid? Manager; // this is an ID of another user in the system
}

Служба вызовет AutoMapper:

var userModel = Mapper.Map<UserViewModel, UserModel>(viewModel);

для сопоставления с этим классом:

class UserModel {
    // other fields...
    UserModel Manager;
}

с помощью преобразователя типов, который выгляделдругого пользователя в базе данных по GUID менеджера из модели представления.Преобразователи типов внедряются в зависимости с помощью Ninject, и все отлично работает в веб-приложении.

Я пытаюсь написать модульный тест, который будет использовать фиктивные репозитории с преобразователями типов.AutoMapper может быть настроен для создания служб с использованием предоставленной пользователем функции:

Mapper.Initialize(cfg =>
{
    cfg.ConstructServicesUsing(type => Kernel.GetService(type));
});

В веб-приложении Kernel is Ninject, для тестов я решил предоставить свой собственный метод, который возвращает экземпляры запрошенных типов из словаря:

Dictionary<Type, object> typeDict = new Dictionary<Type, object>()
{
    { typeof(IRepository), new MockRepository() }
};

...
Mapper.Initialize(cfg =>
{
    cfg.ConstructServicesUsing(type => { return typeDict[type]; } );
});

Идея заключалась в том, что когда AutoMapper настроен на преобразование Guid в объект, подобный этому:

Mapper.CreateMap<Guid?, TDropDown>()
       .ConvertUsing<GuidToSelectListValueConverter<TDropDown>>();

, и ему необходимо создать новый экземпляр GuidToSelectListValueConverter и внедрить его с помощью репозитория.запросит тип IRepository с помощью метода, настроенного в ConstructServicesUsing.

Вместо этого AutoMapper фактически пытается получить экземпляр типа GuidToSelectListValueConverter.Это означает, что я отвечаю за создание нового экземпляра конвертера типов и добавление его в зависимости.Вместо этого мой typeDict должен выглядеть следующим образом:

Dictionary<Type, object> typeDict = new Dictionary<Type, object>()
{
    { typeof(GuidToSelectListValueConverter), new GuidToSelectListValueConverter(
               new MockRepository()) }
};

У меня есть много преобразователей типов, и ручная запись функций распознавателя для них не представляется осуществимой.Так что это заставило меня задуматься.Было бы неправильно просто настроить Ninject для разрешения этих зависимостей в моем модульном тесте?Я мог бы связать IRepository с MockRepository, а затем, когда мне понадобится экземпляр службы, просто вызвать Kernel.GetService (typeof (MyService)) и заставить Ninject внедрить его с помощью MockRepository.Дополнительным преимуществом является то, что AutoMapper будет также использовать Ninject для получения экземпляров преобразователей типов и преобразователей значений:

// configure Ninject
Kernel.Bind<IRepository>().To<MockRepository>();
// initialize AutoMapper
Mapper.Initialize(cfg => 
{ 
    cfg.ConstructServicesUsing(t => Kernel.GetService(t)); 
});
// create an instance of service to test
var service = Kernel.GetService(IMyService);
// do work
var result = service.DoWork();
// analyze result

Теперь, когда метод DoWork вызывает метод Mapper. Преобразователи типов создаются с использованием Ninject.

Любые предложения, идеи и советы передового опыта приветствуются.

Спасибо!

1 Ответ

1 голос
/ 09 августа 2011

Проверьте этот пост об использовании IoC с AutoMapper .

Это помогло мне двигаться в правильном направлении, используя IMappingEngine вместо Mapper в моих контроллерах.

...