简体   繁体   English

单元测试依赖于AutoMapper和类型转换器的ASP.NET MVC应用程序服务

[英]Unit testing ASP.NET MVC application services that rely on AutoMapper and type converters

The service layer of my ASP.NET MVC 3 application uses AutoMapper to map view models to business objects. 我的ASP.NET MVC 3应用程序的服务层使用AutoMapper将视图模型映射到业务对象。 I implemented type converters to convert object IDs submitted in post/get request to business objects.Type converters look up entities in the database by IDs and rebuild business from those. 我实现了类型转换器,将发布/获取请求中提交的对象ID转换为业务对象。类型转换器通过ID查找数据库中的实体,并从中重建业务。 For example: 例如:

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

A service will call AutoMapper: 服务将调用AutoMapper:

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

to map to this class: 映射到此类:

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

with the help of type converter that looked up another user in the database by Manager GUID from view model. 借助类型转换器的帮助,该类型转换器通过Manager GUID从视图模型中查找数据库中的另一个用户。 Type converters are injected with dependencies using Ninject and everything works fine in the web application. 使用Ninject向类型转换器注入依赖项,并且在Web应用程序中一切正常。

I'm trying to write unit-test that will use mock repositories with type converters. 我正在尝试编写将使用模拟存储库和类型转换器的单元测试。 AutoMapper can be configured to construct services using a user provided function: 可以将AutoMapper配置为使用用户提供的功能来构造服务:

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

In web application Kernel is Ninject, for tests I decided to provided my own method that returns instances of requested types from a dictionary: 在Web应用程序中,内核是Ninject,对于测试,我决定提供自己的方法,该方法从字典中返回请求的类型的实例:

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

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

The idea was that when AutoMapper is configured to convert a Guid to an object like this: 这个想法是,当将AutoMapper配置为将Guid转换为这样的对象时:

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

and needs to create an new instance of GuidToSelectListValueConverter and inject it with a repository it will request the IRepository type using method configured in ConstructServicesUsing. 并且需要创建一个GuidToSelectListValueConverter的新实例,并向其注入存储库,它将使用ConstructServicesUsing中配置的方法请求IRepository类型。

Instead AutoMapper actually tries to obtain an instance of GuidToSelectListValueConverter type. 相反,AutoMapper实际上尝试获取GuidToSelectListValueConverter类型的实例。 Which means I become responsible for creating a new instance of type converter and injecting it with dependencies. 这意味着我要负责创建类型转换器的新实例并将其注入依赖项。 My typeDict should look like this instead: 我的typeDict应该看起来像这样:

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

I do have a lot of type converters and manually write resolver functions for those doesn't seem feasible. 我确实有很多类型转换器,并且为那些似乎不可行的手动编写解析器功能。 So that got me thinking. 所以这让我开始思考。 Would it be wrong to simply configure Ninject for to resolve these dependencies in my unit test? 简单地配置Ninject来解决我的单元测试中的这些依赖关系是否会出错? I could bind IRepository to MockRepository and then when I need an instance of service simply call Kernel.GetService(typeof(MyService)) and have Ninject inject it with MockRepository. 我可以将IRepository绑定到MockRepository,然后当我需要服务实例时,只需调用Kernel.GetService(typeof(MyService))并让Ninject用MockRepository注入它。 The added benefit is that AutoMapper will also use Ninject to get instances of type converters and value resolvers: 额外的好处是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

Now when DoWork method calls Mapper.Map type converters are created using Ninject. 现在,当DoWork方法调用Mapper.Map时,将使用Ninject创建类型转换器。

Any suggestions, ideas and best practices advices are welcome. 欢迎任何建议,想法和最佳实践建议。

Thank you! 谢谢!

Check out this post on using IoC with AutoMapper . 查看有关将IoC与AutoMapper结合使用的信息

This helped me get going in the right direction by using IMappingEngine instead of Mapper in my controllers. 通过在控制器中使用IMappingEngine而不是Mapper ,这有助于我朝正确的方向前进。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM