使用Automapper和xUnit进行单元测试时,Mapper已初始化问题

时间:2019-04-10 15:39:32

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

我对使用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>

谢谢。

2 个答案:

答案 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();
}