【问题标题】:Mapper already initialized issue when using Automapper and xUnit for unit testing使用 Automapper 和 xUnit 进行单元测试时,Mapper 已初始化问题
【发布时间】:2019-04-10 15:39:32
【问题描述】:

我对使用 xUnit 进行单元测试有点陌生,但我在使用 AutoMapper 时遇到了一些问题。我遇到了 Mapper 已初始化 问题。

我正在使用 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() - 这里的一些答案表明: Automapper - Mapper already initialized error

在 VS 的测试资源管理器中,当我选择一个测试类并选择“运行选定的测试”时,它们都通过了。但是,当我选择主要的“全部运行”时,一些测试会失败并显示消息 Mapper 已初始化。每次都是不同类别的不同测试失败。

我假设为不同的方法创建了不同的线程,但它们都试图初始化相同的映射器实例,这会引发错误。

但是,我不确定我应该在哪里调用初始化在一个(并且只有一个)位置,并且我的所有测试类都使用相同的初始化(就像我在 Startup.cs 配置方法中所做的那样)。

提前致谢。

【问题讨论】:

  • 共享资源的竞争条件。也使用async Task 而不是async void
  • RegisterMaps 只能调用一次。
  • @Nkosi - 我同意这是共享资源的竞争条件。你有什么建议我可以调用 RegisterMaps 以便在我的所有测试类中的所有测试中只调用一次?
  • 看看我这里提供的答案stackoverflow.com/a/39868221/5233410
  • 嘿@MarioMucalo,看看这个stackoverflow.com/questions/47241708/…

标签: c# unit-testing .net-core automapper xunit


【解决方案1】:

感谢@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,我会在其中详细介绍和解释,所以如果您需要更多信息,请阅读。

【讨论】:

  • 谢谢,@LucianBargaonu - 对于这种特殊情况和“映射器已初始化”错误,这并不真正相关,但我同意这是将 AutoMapper 添加到项目中的一种更简洁的方法。
【解决方案2】:

在其构造函数中初始化 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();
}

【讨论】:

    猜你喜欢
    • 2019-10-20
    • 1970-01-01
    • 2017-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    相关资源
    最近更新 更多