[英]Mapper already initialized issue when using Automapper and xUnit for unit testing
I am a bit new to unit testing with xUnit, and I have some problems with AutoMapper.我对使用 xUnit 进行单元测试有点陌生,并且我在使用 AutoMapper 时遇到了一些问题。 I am getting the Mapper already initialized issue.
我收到Mapper 已经初始化的问题。
I am using Automapper 8.0.0., ASP.NET Core 2.2 and xUnit 2.4.1.我正在使用 Automapper 8.0.0.、ASP.NET Core 2.2 和 xUnit 2.4.1。
I am writing unit tests for my controllers.我正在为我的控制器编写单元测试。 I have unit tests in 3 different classes.
我在 3 个不同的类中有单元测试。 Each class looks basically like this:
每个类看起来基本上是这样的:
/* 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. */
All three classes are similar and are basic tests for controllers.所有三个类都相似并且是控制器的基本测试。 All controllers are using AutoMapper.
所有控制器都使用 AutoMapper。 I am using the same static class MapperConfig to register my mappings:
我使用相同的静态类 MapperConfig 来注册我的映射:
public static class MapperConfig
{
public static void RegisterMaps()
{
AutoMapper.Mapper.Initialize(config =>
{
config.CreateMap<SomeClass, SomeClassViewModel>();
config.CreateMap<SomeClassViewModel, SomeClass>();
});
}
}
I call this method in the constructor of each of the 3 test classes.我在 3 个测试类中的每一个的构造函数中调用此方法。 Before calling it, I call the Mapper.Reset() - some answers here suggest that: Automapper - Mapper already initialized error
在调用它之前,我调用 Mapper.Reset() - 这里的一些答案表明: Automapper - Mapper already initialized error
In the Test Explorer in VS when I select one test class and choose "Run selected tests", they all pass.在 VS 的测试资源管理器中,当我选择一个测试类并选择“运行选定的测试”时,它们都通过了。 However, when I select the main "Run all", some tests fail with the message Mapper already initialized .
但是,当我选择主要的“全部运行”时,一些测试失败并显示消息Mapper already initialized 。 And each time it is different tests in different classes that fail.
每次失败的都是不同类中的不同测试。
I assume that different threads are created for different methods, but they are all trying to initialize the same mapper instance which throws an error.我假设为不同的方法创建了不同的线程,但它们都试图初始化引发错误的相同映射器实例。
However, I am not sure where am I supposed to call the initialization in one (and only one) place and have that same initialization be used for all my test classes (like I do in Startup.cs Configure method).但是,我不确定我应该在哪里(并且只有一个)地方调用初始化,并将相同的初始化用于我的所有测试类(就像我在 Startup.cs Configure 方法中所做的那样)。
Thanks in advance.提前致谢。
Thanks to @Nkosi and @Dmitry Pavlov for their ideas.感谢@Nkosi 和@Dmitry Pavlov 的想法。
What I ended up doing was:我最终做的是:
1) Moving to instance API of the AutoMapper 1) 转向 AutoMapper 的实例 API
This meant that the AutoMapper is now defined in Startup.cs in ConfigureServices method as:这意味着 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);
//...
}
And injected into controllers like:并注入控制器,如:
public class ItemsInstanceController : ControllerBase
{
private readonly IItemService _itemService;
private readonly IMapper _mapper;
public ItemsInstanceController(IItemService itemService, IMapper mapper)
{
_itemService = itemService;
_mapper = mapper;
}
//...
}
2) However, without spinning up a special test server, startup.cs methods are not run when tests are executed. 2) 但是,如果没有启动特殊的测试服务器,则在执行测试时不会运行 startup.cs 方法。 So, for testing purposes, I ended up writing a small helper class implementing a singleton pattern on AutoMapper:
因此,出于测试目的,我最终编写了一个在 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) Now in my tests I just needed to create the controller like this: 3)现在在我的测试中,我只需要像这样创建控制器:
controller = new ItemsInstanceController(itemServiceMock.Object, AutomapperSingleton.Mapper);
and the initialization was never run twice, only once on constructing the instance of the AutoMapper.并且初始化从未运行过两次,仅在构造 AutoMapper 的实例时运行一次。
I have written a blog post where I go into much more details and explanations, so if you need more information, please go and read it.我写了一篇博客文章,其中详细介绍了更多细节和解释,因此如果您需要更多信息,请阅读。
Lazy loading of a wrapper class which initializes AutoMapper in it's constructor also works in the following manner:延迟加载在其构造函数中初始化 AutoMapper 的包装类也以以下方式工作:
public class StaticDependencies
{
public static Lazy<StaticDependencies> Initializer = new Lazy<StaticDependencies>();
public StaticDependencies()
{
MapperConfig.RegisterMaps();
}
public void AssertSetup()
{
// No Op
}
}
Then, in the constructor of your XUnit Test, simply refer to the static lazy loaded object:然后,在 XUnit 测试的构造函数中,只需引用静态延迟加载对象:
public ControllerGetTests()
{
/// Initialize AutoMapper
StaticDependencies.Initializer.Value.AssertSetup();
/* Some mocking code here using Moq */
_controller = new MyController();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.