我对使用xUnit进行单元测试有点陌生,并且在使用AutoMapper时遇到了一些问题。我遇到了映射器已经初始化的问题。
我正在使用Automapper 8.0.0,ASP.NET Core 2.2和xUnit 2.4.1。
我正在为控制器编写单元测试。 我有3个不同班级的单元测试。每个类基本上如下所示:
/* Constructor */
public ControllerGetTests()
{
/// Initialize AutoMapper
AutoMapper.Mapper.Reset();
MapperConfig.RegisterMaps();
/* Some mocking code here using Moq */
_controller = new MyController();
}
[Fact]
public async void Get_WhenCalled_ReturnsOkResult()
{
// Act
var okResult = await _controller.Get();
// Assert
Assert.IsType<OkObjectResult>(okResult);
}
/* etc. */
这三个类都是相似的,并且是控制器的基本测试。 所有控制器都使用AutoMapper。 我正在使用相同的静态类MapperConfig来注册我的映射:
public static class MapperConfig
{
public static void RegisterMaps()
{
AutoMapper.Mapper.Initialize(config =>
{
config.CreateMap<SomeClass, SomeClassViewModel>();
config.CreateMap<SomeClassViewModel, SomeClass>();
});
}
}
我在3个测试类的构造函数中调用此方法。 在调用它之前,我先调用Mapper.Reset()-这里的一些答案表明: documentation
在VS的“测试资源管理器”中,当我选择一个测试类并选择“运行选定的测试”时,它们全部通过。但是,当我选择主要的“全部运行”时,某些测试失败,并显示消息映射器已初始化。每次在不同的类中进行不同的测试都会失败。
我假设为不同的方法创建了不同的线程,但是它们都试图初始化引发错误的同一个mapper实例。
但是,我不确定我应该在哪里(只有一个)调用初始化,是否对所有测试类都使用相同的初始化(就像我在Startup.cs Configure方法中所做的那样)。 / p>
谢谢。
答案 0 :(得分:0)
感谢@Nkosi和@Dmitry Pavlov的想法。
我最终要做的是:
1)移至AutoMapper的实例API
这意味着AutoMapper现在在Startup.cs中的ConfigureServices方法中定义为:
public void ConfigureServices(IServiceCollection services)
{
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MyMappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
//...
}
并注入到像这样的控制器中
public class ItemsInstanceController : ControllerBase
{
private readonly IItemService _itemService;
private readonly IMapper _mapper;
public ItemsInstanceController(IItemService itemService, IMapper mapper)
{
_itemService = itemService;
_mapper = mapper;
}
//...
}
2)但是,在不启动特殊测试服务器的情况下,执行测试时不会运行startup.cs方法。因此,出于测试目的,我最终编写了一个小的帮助程序类,在AutoMapper上实现了单例模式:
public class AutomapperSingleton
{
private static IMapper _mapper;
public static IMapper Mapper
{
get
{
if (_mapper == null)
{
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MyMappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
_mapper = mapper;
}
return _mapper;
}
}
}
3)现在,在我的测试中,我只需要创建这样的控制器:
controller = new ItemsInstanceController(itemServiceMock.Object, AutomapperSingleton.Mapper);
并且初始化从未运行两次,而是在构造AutoMapper实例时仅运行过一次。
我写了blog post,其中有更多详细信息和说明,因此,如果您需要更多信息,请阅读。
答案 1 :(得分:0)
延迟装入包装器类以在其构造函数中初始化AutoMapper的方式也可以按以下方式进行:
public class StaticDependencies
{
public static Lazy<StaticDependencies> Initializer = new Lazy<StaticDependencies>();
public StaticDependencies()
{
MapperConfig.RegisterMaps();
}
public void AssertSetup()
{
// No Op
}
}
然后,在您的XUnit Test的构造函数中,只需引用静态延迟加载对象:
public ControllerGetTests()
{
/// Initialize AutoMapper
StaticDependencies.Initializer.Value.AssertSetup();
/* Some mocking code here using Moq */
_controller = new MyController();
}