You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单元测试中手动模拟基于AutoMapper实现的IObjectMapper

如何在单元测试中手动模拟基于AutoMapper的ABP IObjectMapper(不加载ABP框架)

问题场景

在ABP项目中实现了OrderRepository,它依赖ABP提供的IObjectMapper(默认基于AutoMapper实现,已通过DbEntityToDomainModelProfile配置了OrderDbEntity到Order的映射规则)。当前使用NSubstitute编写单元测试,不想加载ABP框架,同时希望复用现有AutoMapper映射配置,避免为每个测试手动模拟Map方法的返回值。

解决方案

核心思路是直接基于现有AutoMapper配置创建真实的IMapper实例,让IObjectMapper的替代实例(Substitute)委托该实例处理映射逻辑,无需依赖ABP框架。

具体步骤

  1. 初始化AutoMapper配置
    加载项目中已定义的DbEntityToDomainModelProfile,创建MapperConfiguration并验证配置有效性(可选但推荐)。

  2. 生成真实IMapper实例
    通过配置生成可执行映射的IMapper实例。

  3. 配置IObjectMapper替代实例
    让NSubstitute生成的IObjectMapper替代实例,在调用Map方法时,委托给真实的IMapper执行映射操作。

完整测试代码示例

using Xunit;
using NSubstitute;
using AutoMapper;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class OrderRepositoryTests
{
    public class GetAllAsync
    {
        [Fact]
        public async Task WhenThereIsNoOrder_ShouldReturnEmptyList()
        {
            // Arrange
            // 1. 初始化AutoMapper配置,复用现有映射Profile
            var mapperConfig = new MapperConfiguration(cfg => 
            {
                cfg.AddProfile<DbEntityToDomainModelProfile>();
            });
            // 验证映射配置,确保无错误
            mapperConfig.AssertConfigurationIsValid();
            
            // 2. 创建真实的IMapper实例
            var realMapper = mapperConfig.CreateMapper();

            // 模拟DbContext返回空订单列表
            var mockDbContext = Substitute.For<OrderingDbContext>();
            mockDbContext.Orders.ToListAsync(Arg.Any<CancellationToken>())
                .Returns(Task.FromResult(new List<OrderDbEntity>()));

            // 3. 配置IObjectMapper替代实例,委托真实IMapper处理映射
            var mockObjectMapper = Substitute.For<IObjectMapper>();
            mockObjectMapper
                .Map<IEnumerable<OrderDbEntity>, IEnumerable<Order>>(Arg.Any<IEnumerable<OrderDbEntity>>())
                .Returns(callInfo => 
                {
                    var sourceEntities = callInfo.Arg<IEnumerable<OrderDbEntity>>();
                    return realMapper.Map<IEnumerable<Order>>(sourceEntities);
                });

            var sut = new OrderRepository(mockDbContext, mockObjectMapper);

            // Act
            var orders = await sut.GetAllAsync();

            // Assert
            Assert.Empty(orders);
            await mockDbContext.Orders.Received(1).ToListAsync(Arg.Any<CancellationToken>());
            mockObjectMapper.Received(1).Map<IEnumerable<OrderDbEntity>, IEnumerable<Order>>(Arg.Any<IEnumerable<OrderDbEntity>>());
        }
    }
}

复用AutoMapper配置的优化

如果多个测试方法都需要使用AutoMapper配置,可以将初始化逻辑提取到IClassFixture中,避免重复初始化:

public class OrderRepositoryTests : IClassFixture<AutoMapperFixture>
{
    private readonly IMapper _realMapper;

    public OrderRepositoryTests(AutoMapperFixture fixture)
    {
        _realMapper = fixture.Mapper;
    }

    // 测试方法...
}

public class AutoMapperFixture
{
    public IMapper Mapper { get; }

    public AutoMapperFixture()
    {
        var mapperConfig = new MapperConfiguration(cfg => 
        {
            cfg.AddProfile<DbEntityToDomainModelProfile>();
        });
        mapperConfig.AssertConfigurationIsValid();
        Mapper = mapperConfig.CreateMapper();
    }
}

这种方式既保留了单元测试的独立性,又复用了现有映射规则,避免了手动模拟映射返回值的冗余工作。

内容的提问来源于stack exchange,提问作者David Liang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:43:27